mirror of
https://github.com/Palm1r/QodeAssist.git
synced 2026-07-24 04:01:04 -04:00
feat: add support acp in common chat (#369)
This commit is contained in:
165
sources/acp/AgentCatalogStore.cpp
Normal file
165
sources/acp/AgentCatalogStore.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright (C) 2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
#include "AgentCatalogStore.hpp"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QSaveFile>
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <logger/Logger.hpp>
|
||||
|
||||
#include "acp/AgentRegistryParser.hpp"
|
||||
|
||||
namespace QodeAssist::Acp {
|
||||
|
||||
namespace {
|
||||
|
||||
QByteArray readFile(const QString &path)
|
||||
{
|
||||
QFile file(path);
|
||||
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
|
||||
return {};
|
||||
return file.readAll();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AgentCatalogStore::AgentCatalogStore(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_network(new QNetworkAccessManager(this))
|
||||
{}
|
||||
|
||||
QString AgentCatalogStore::userAgentsDirectory()
|
||||
{
|
||||
const QString path = QStringLiteral("%1/qodeassist/agents")
|
||||
.arg(Core::ICore::userResourcePath().toFSPathString());
|
||||
QDir().mkpath(path);
|
||||
return path;
|
||||
}
|
||||
|
||||
QString AgentCatalogStore::registryCachePath()
|
||||
{
|
||||
const QString directory
|
||||
= QStringLiteral("%1/qodeassist").arg(Core::ICore::userResourcePath().toFSPathString());
|
||||
QDir().mkpath(directory);
|
||||
return directory + QStringLiteral("/acp-registry-cache.json");
|
||||
}
|
||||
|
||||
QUrl AgentCatalogStore::registryUrl()
|
||||
{
|
||||
return QUrl(
|
||||
QStringLiteral("https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json"));
|
||||
}
|
||||
|
||||
QString AgentCatalogStore::bundledSnapshotPath()
|
||||
{
|
||||
return QStringLiteral(":/resources/agents/acp-registry-snapshot.json");
|
||||
}
|
||||
|
||||
bool AgentCatalogStore::hasCachedRegistry() const
|
||||
{
|
||||
return QFileInfo::exists(registryCachePath());
|
||||
}
|
||||
|
||||
void AgentCatalogStore::reload()
|
||||
{
|
||||
m_warnings.clear();
|
||||
loadBundledSnapshot();
|
||||
loadRegistryCache();
|
||||
loadUserFiles();
|
||||
emit catalogChanged();
|
||||
}
|
||||
|
||||
void AgentCatalogStore::loadBundledSnapshot()
|
||||
{
|
||||
const AgentParseResult result = AgentRegistryParser::parse(
|
||||
readFile(bundledSnapshotPath()),
|
||||
AgentSource::BundledSnapshot,
|
||||
QStringLiteral("bundled snapshot"));
|
||||
|
||||
m_warnings.append(result.warnings);
|
||||
m_catalog.setLayer(AgentSource::BundledSnapshot, result.agents);
|
||||
}
|
||||
|
||||
void AgentCatalogStore::loadRegistryCache()
|
||||
{
|
||||
const QString path = registryCachePath();
|
||||
if (!QFileInfo::exists(path)) {
|
||||
m_catalog.setLayer(AgentSource::LiveRegistry, {});
|
||||
return;
|
||||
}
|
||||
|
||||
const AgentParseResult result = AgentRegistryParser::parse(
|
||||
readFile(path), AgentSource::LiveRegistry, QStringLiteral("registry cache"));
|
||||
|
||||
m_warnings.append(result.warnings);
|
||||
m_catalog.setLayer(AgentSource::LiveRegistry, result.agents);
|
||||
}
|
||||
|
||||
void AgentCatalogStore::loadUserFiles()
|
||||
{
|
||||
const QDir directory(userAgentsDirectory());
|
||||
const QStringList files
|
||||
= directory.entryList({QStringLiteral("*.json")}, QDir::Files, QDir::Name);
|
||||
|
||||
QList<AgentDefinition> agents;
|
||||
for (const QString &file : files) {
|
||||
const QString path = directory.filePath(file);
|
||||
const AgentParseResult result
|
||||
= AgentRegistryParser::parse(readFile(path), AgentSource::UserFile, file);
|
||||
m_warnings.append(result.warnings);
|
||||
agents.append(result.agents);
|
||||
}
|
||||
|
||||
m_catalog.setLayer(AgentSource::UserFile, agents);
|
||||
}
|
||||
|
||||
void AgentCatalogStore::refreshFromRegistry()
|
||||
{
|
||||
if (m_reply)
|
||||
return;
|
||||
|
||||
m_reply = m_network->get(QNetworkRequest(registryUrl()));
|
||||
connect(m_reply, &QNetworkReply::finished, this, [this]() {
|
||||
QNetworkReply *reply = m_reply;
|
||||
m_reply = nullptr;
|
||||
reply->deleteLater();
|
||||
|
||||
if (reply->error() != QNetworkReply::NoError) {
|
||||
emit refreshFinished(false, reply->errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
const QByteArray payload = reply->readAll();
|
||||
const AgentParseResult result = AgentRegistryParser::parse(
|
||||
payload, AgentSource::LiveRegistry, QStringLiteral("registry"));
|
||||
|
||||
if (result.agents.isEmpty()) {
|
||||
emit refreshFinished(false, tr("The registry response contained no agents."));
|
||||
return;
|
||||
}
|
||||
|
||||
QSaveFile cache(registryCachePath());
|
||||
if (!cache.open(QIODevice::WriteOnly) || cache.write(payload) != payload.size()
|
||||
|| !cache.commit()) {
|
||||
emit refreshFinished(
|
||||
false, tr("Cannot write the registry cache: %1").arg(cache.errorString()));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_MESSAGE(QString("ACP registry refreshed: %1 agents").arg(result.agents.size()));
|
||||
|
||||
reload();
|
||||
emit refreshFinished(true, {});
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace QodeAssist::Acp
|
||||
Reference in New Issue
Block a user