cli: drop exceptions from KompanionApp
This commit is contained in:
parent
a84af5d464
commit
8479c23234
|
|
@ -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();
|
||||||
}
|
}
|
||||||
if (!query.next()) return false;
|
return false;
|
||||||
return query.value(0).toBool();
|
}
|
||||||
|
if (!query.next()) {
|
||||||
|
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)) {
|
||||||
|
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";
|
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";
|
out << "Schema not found; applying migrations...\n";
|
||||||
if (applySchemaFiles(db, out, verbose) && schemaExists(db)) {
|
if (!applySchemaFiles(db, out, verbose)) {
|
||||||
out << "Schema initialized successfully.\n";
|
out << "Schema application reported errors.\n";
|
||||||
success = true;
|
db.close();
|
||||||
} else {
|
db = QSqlDatabase();
|
||||||
|
QSqlDatabase::removeDatabase(connName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!schemaExists(db, &exists, &err) || !exists) {
|
||||||
out << "Schema still missing after applying migrations.\n";
|
out << "Schema still missing after applying migrations.\n";
|
||||||
success = false;
|
if (!err.isEmpty()) {
|
||||||
}
|
out << "Last error: " << err << "\n";
|
||||||
}
|
|
||||||
} 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;
|
return false;
|
||||||
|
}
|
||||||
|
db.close();
|
||||||
|
db = QSqlDatabase();
|
||||||
|
QSqlDatabase::removeDatabase(connName);
|
||||||
|
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,14 +783,20 @@ 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";
|
||||||
}
|
}
|
||||||
|
|
@ -766,8 +806,4 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
std::cout << response << std::endl;
|
std::cout << response << std::endl;
|
||||||
return 0;
|
return 0;
|
||||||
} catch (const std::exception& ex) {
|
|
||||||
std::cerr << "Error: " << ex.what() << "\n";
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue