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
}
std::string readFileUtf8(const QString& path) {
bool readFileUtf8(const QString& path, std::string& out, QString* error) {
QFile file(path);
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();
return QString::fromUtf8(data).toStdString();
out = QString::fromUtf8(data).toStdString();
return true;
}
bool looksLikeFile(const QString& value) {
@ -307,13 +311,20 @@ bool testConnection(const std::string& dsn, QString* error = nullptr) {
return opened;
}
bool schemaExists(QSqlDatabase& db) {
bool schemaExists(QSqlDatabase& db, bool* exists, QString* error) {
QSqlQuery query(db);
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();
}
if (!query.next()) return false;
return query.value(0).toBool();
return false;
}
if (!query.next()) {
if (exists) *exists = false;
return true;
}
if (exists) *exists = query.value(0).toBool();
return true;
}
bool applySchemaFiles(QSqlDatabase& db,
@ -380,29 +391,48 @@ bool ensureSchema(const std::string& dsn,
return false;
}
bool success = false;
try {
if (schemaExists(db)) {
bool exists = false;
QString err;
if (!schemaExists(db, &exists, &err)) {
out << "Failed to check schema: " << err << "\n";
db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
return false;
}
if (exists) {
if (verbose) out << "Schema already present.\n";
success = true;
db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
return true;
}
if (!success) {
out << "Schema not found; applying migrations...\n";
if (applySchemaFiles(db, out, verbose) && schemaExists(db)) {
out << "Schema initialized successfully.\n";
success = true;
} else {
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";
success = false;
}
}
} catch (const std::exception& ex) {
out << "Failed to ensure schema: " << ex.what() << "\n";
if (!err.isEmpty()) {
out << "Last error: " << err << "\n";
}
db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
return success;
return false;
}
db.close();
db = QSqlDatabase();
QSqlDatabase::removeDatabase(connName);
out << "Schema initialized successfully.\n";
return true;
}
std::optional<std::string> autoDetectDsn() {
@ -578,50 +608,54 @@ int runInteractiveSession(KomMcpServer& server,
out.flush();
}
try {
const std::string response = server.dispatch(toolName, payload);
if (verbose) {
out << "[response] " << QString::fromStdString(response) << "\n";
} else {
out << QString::fromStdString(response) << "\n";
}
} catch (const std::exception& ex) {
out << "Error: " << ex.what() << "\n";
}
}
return 0;
}
std::string resolveRequestPayload(const QCommandLineParser& parser,
bool resolveRequestPayload(const QCommandLineParser& parser,
const QStringList& positional,
const QCommandLineOption& requestOption,
const QCommandLineOption& stdinOption) {
const QCommandLineOption& stdinOption,
std::string& payloadOut,
QString* error) {
if (parser.isSet(stdinOption)) {
return readAll(std::cin);
payloadOut = readAll(std::cin);
return true;
}
if (parser.isSet(requestOption)) {
const QString arg = parser.value(requestOption);
if (arg == "-" || parser.isSet(stdinOption)) {
return readAll(std::cin);
payloadOut = readAll(std::cin);
return true;
}
if (looksLikeFile(arg)) {
return readFileUtf8(arg);
return readFileUtf8(arg, payloadOut, error);
}
return arg.toStdString();
payloadOut = arg.toStdString();
return true;
}
if (positional.size() > 1) {
const QString arg = positional.at(1);
if (arg == "-") {
return readAll(std::cin);
payloadOut = readAll(std::cin);
return true;
}
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) {
@ -749,14 +783,20 @@ int main(int argc, char** argv) {
}
std::string request;
try {
request = resolveRequestPayload(parser, positional, requestOption, stdinOption);
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
QString requestError;
if (!resolveRequestPayload(parser,
positional,
requestOption,
stdinOption,
request,
&requestError)) {
const QString message = requestError.isEmpty()
? QStringLiteral("Failed to resolve request payload.")
: requestError;
std::cerr << "Error: " << message.toStdString() << "\n";
return 1;
}
try {
if (verbose) {
std::cerr << "[request] " << request << "\n";
}
@ -766,8 +806,4 @@ int main(int argc, char** argv) {
}
std::cout << response << std::endl;
return 0;
} catch (const std::exception& ex) {
std::cerr << "Error: " << ex.what() << "\n";
}
return 1;
}

View File

@ -74,7 +74,15 @@ inline std::string runCommandCapture(const char* cmd, std::size_t maxBytes = 819
(void)maxBytes;
return "git status capture not supported on this platform";
#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) {
return "git status unavailable";
}