cli: drop exceptions from KompanionApp

This commit is contained in:
Χγφτ Kompanion 2025-10-15 18:44:00 +02:00
parent a84af5d464
commit 8479c23234
2 changed files with 108 additions and 64 deletions

View File

@ -133,13 +133,17 @@ void writeDsnToConfig(const std::string& dsn) {
#endif #endif
} }
std::string readFileUtf8(const QString& path) { bool readFileUtf8(const QString& path, std::string& out, QString* error) {
QFile file(path); QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
throw std::runtime_error(QString("Unable to open request file: %1").arg(path).toStdString()); if (error) {
*error = QStringLiteral("Unable to open request file: %1").arg(path);
}
return false;
} }
const QByteArray data = file.readAll(); const QByteArray data = file.readAll();
return QString::fromUtf8(data).toStdString(); out = QString::fromUtf8(data).toStdString();
return true;
} }
bool looksLikeFile(const QString& value) { bool looksLikeFile(const QString& value) {
@ -307,13 +311,20 @@ bool testConnection(const std::string& dsn, QString* error = nullptr) {
return opened; return opened;
} }
bool schemaExists(QSqlDatabase& db) { bool schemaExists(QSqlDatabase& db, bool* exists, QString* error) {
QSqlQuery query(db); QSqlQuery query(db);
if (!query.exec(QStringLiteral("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname='public' AND tablename='memory_items')"))) { if (!query.exec(QStringLiteral("SELECT EXISTS (SELECT 1 FROM pg_tables WHERE schemaname='public' AND tablename='memory_items')"))) {
throw std::runtime_error(query.lastError().text().toStdString()); if (error) {
*error = query.lastError().text();
}
return false;
} }
if (!query.next()) return false; if (!query.next()) {
return query.value(0).toBool(); if (exists) *exists = false;
return true;
}
if (exists) *exists = query.value(0).toBool();
return true;
} }
bool applySchemaFiles(QSqlDatabase& db, bool applySchemaFiles(QSqlDatabase& db,
@ -380,29 +391,48 @@ bool ensureSchema(const std::string& dsn,
return false; return false;
} }
bool success = false; bool exists = false;
try { QString err;
if (schemaExists(db)) { if (!schemaExists(db, &exists, &err)) {
if (verbose) out << "Schema already present.\n"; out << "Failed to check schema: " << err << "\n";
success = true; db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
return false;
}
if (exists) {
if (verbose) out << "Schema already present.\n";
db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
return true;
}
out << "Schema not found; applying migrations...\n";
if (!applySchemaFiles(db, out, verbose)) {
out << "Schema application reported errors.\n";
db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
return false;
}
if (!schemaExists(db, &exists, &err) || !exists) {
out << "Schema still missing after applying migrations.\n";
if (!err.isEmpty()) {
out << "Last error: " << err << "\n";
} }
if (!success) { db.close();
out << "Schema not found; applying migrations...\n"; db = QSqlDatabase();
if (applySchemaFiles(db, out, verbose) && schemaExists(db)) { QSqlDatabase::removeDatabase(connName);
out << "Schema initialized successfully.\n"; return false;
success = true;
} else {
out << "Schema still missing after applying migrations.\n";
success = false;
}
}
} catch (const std::exception& ex) {
out << "Failed to ensure schema: " << ex.what() << "\n";
} }
db.close(); db.close();
db = QSqlDatabase(); db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName); QSqlDatabase::removeDatabase(connName);
return success; out << "Schema initialized successfully.\n";
return true;
} }
std::optional<std::string> autoDetectDsn() { std::optional<std::string> autoDetectDsn() {
@ -578,50 +608,54 @@ int runInteractiveSession(KomMcpServer& server,
out.flush(); out.flush();
} }
try { const std::string response = server.dispatch(toolName, payload);
const std::string response = server.dispatch(toolName, payload); if (verbose) {
if (verbose) { out << "[response] " << QString::fromStdString(response) << "\n";
out << "[response] " << QString::fromStdString(response) << "\n"; } else {
} else { out << QString::fromStdString(response) << "\n";
out << QString::fromStdString(response) << "\n";
}
} catch (const std::exception& ex) {
out << "Error: " << ex.what() << "\n";
} }
} }
return 0; return 0;
} }
std::string resolveRequestPayload(const QCommandLineParser& parser, bool resolveRequestPayload(const QCommandLineParser& parser,
const QStringList& positional, const QStringList& positional,
const QCommandLineOption& requestOption, const QCommandLineOption& requestOption,
const QCommandLineOption& stdinOption) { const QCommandLineOption& stdinOption,
std::string& payloadOut,
QString* error) {
if (parser.isSet(stdinOption)) { if (parser.isSet(stdinOption)) {
return readAll(std::cin); payloadOut = readAll(std::cin);
return true;
} }
if (parser.isSet(requestOption)) { if (parser.isSet(requestOption)) {
const QString arg = parser.value(requestOption); const QString arg = parser.value(requestOption);
if (arg == "-" || parser.isSet(stdinOption)) { if (arg == "-" || parser.isSet(stdinOption)) {
return readAll(std::cin); payloadOut = readAll(std::cin);
return true;
} }
if (looksLikeFile(arg)) { if (looksLikeFile(arg)) {
return readFileUtf8(arg); return readFileUtf8(arg, payloadOut, error);
} }
return arg.toStdString(); payloadOut = arg.toStdString();
return true;
} }
if (positional.size() > 1) { if (positional.size() > 1) {
const QString arg = positional.at(1); const QString arg = positional.at(1);
if (arg == "-") { if (arg == "-") {
return readAll(std::cin); payloadOut = readAll(std::cin);
return true;
} }
if (looksLikeFile(arg)) { if (looksLikeFile(arg)) {
return readFileUtf8(arg); return readFileUtf8(arg, payloadOut, error);
} }
return arg.toStdString(); payloadOut = arg.toStdString();
return true;
} }
return "{}"; payloadOut = "{}";
return true;
} }
void printToolList(const KomMcpServer& server) { void printToolList(const KomMcpServer& server) {
@ -749,25 +783,27 @@ int main(int argc, char** argv) {
} }
std::string request; std::string request;
try { QString requestError;
request = resolveRequestPayload(parser, positional, requestOption, stdinOption); if (!resolveRequestPayload(parser,
} catch (const std::exception& ex) { positional,
std::cerr << "Error: " << ex.what() << "\n"; requestOption,
stdinOption,
request,
&requestError)) {
const QString message = requestError.isEmpty()
? QStringLiteral("Failed to resolve request payload.")
: requestError;
std::cerr << "Error: " << message.toStdString() << "\n";
return 1; return 1;
} }
try { if (verbose) {
if (verbose) { std::cerr << "[request] " << request << "\n";
std::cerr << "[request] " << request << "\n";
}
const std::string response = server.dispatch(toolName, request);
if (verbose) {
std::cerr << "[response] " << response << "\n";
}
std::cout << response << std::endl;
return 0;
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
} }
return 1; const std::string response = server.dispatch(toolName, request);
if (verbose) {
std::cerr << "[response] " << response << "\n";
}
std::cout << response << std::endl;
return 0;
} }

View File

@ -74,7 +74,15 @@ inline std::string runCommandCapture(const char* cmd, std::size_t maxBytes = 819
(void)maxBytes; (void)maxBytes;
return "git status capture not supported on this platform"; return "git status capture not supported on this platform";
#else #else
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose); struct PipeCloser {
void operator()(FILE* file) const noexcept {
if (file != nullptr) {
pclose(file);
}
}
};
std::unique_ptr<FILE, PipeCloser> pipe(popen(cmd, "r"), PipeCloser{});
if (!pipe) { if (!pipe) {
return "git status unavailable"; return "git status unavailable";
} }