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:
223
sources/acp/AgentRegistryParser.cpp
Normal file
223
sources/acp/AgentRegistryParser.cpp
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright (C) 2026 Petr Mironychev
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// Additional attribution terms under GPLv3 §7(b) apply — see LICENSE
|
||||
|
||||
#include "AgentRegistryParser.hpp"
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
namespace QodeAssist::Acp {
|
||||
|
||||
namespace {
|
||||
|
||||
QStringList readStringList(const QJsonValue &value)
|
||||
{
|
||||
QStringList result;
|
||||
const QJsonArray array = value.toArray();
|
||||
for (const QJsonValue &entry : array) {
|
||||
if (entry.isString())
|
||||
result.append(entry.toString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<AgentEnvVariable> readEnv(const QJsonValue &value)
|
||||
{
|
||||
QList<AgentEnvVariable> result;
|
||||
|
||||
if (value.isObject()) {
|
||||
const QJsonObject object = value.toObject();
|
||||
for (auto it = object.constBegin(); it != object.constEnd(); ++it)
|
||||
result.append({it.key(), it.value().toString()});
|
||||
return result;
|
||||
}
|
||||
|
||||
const QJsonArray array = value.toArray();
|
||||
for (const QJsonValue &entry : array) {
|
||||
const QJsonObject object = entry.toObject();
|
||||
const QString name = object.value(QLatin1String("name")).toString();
|
||||
if (!name.isEmpty())
|
||||
result.append({name, object.value(QLatin1String("value")).toString()});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
AgentDistribution readRunnerDistribution(const QJsonObject &object, AgentDistributionKind kind)
|
||||
{
|
||||
AgentDistribution distribution;
|
||||
distribution.kind = kind;
|
||||
distribution.package = object.value(QLatin1String("package")).toString();
|
||||
distribution.args = readStringList(object.value(QLatin1String("args")));
|
||||
distribution.env = readEnv(object.value(QLatin1String("env")));
|
||||
return distribution;
|
||||
}
|
||||
|
||||
AgentDistribution readBinaryDistribution(const QJsonObject &object)
|
||||
{
|
||||
AgentDistribution distribution;
|
||||
distribution.kind = AgentDistributionKind::Binary;
|
||||
|
||||
for (auto it = object.constBegin(); it != object.constEnd(); ++it) {
|
||||
const QJsonObject entry = it.value().toObject();
|
||||
AgentBinaryTarget target;
|
||||
target.platform = it.key();
|
||||
target.archive = entry.value(QLatin1String("archive")).toString();
|
||||
target.sha256 = entry.value(QLatin1String("sha256")).toString();
|
||||
target.cmd = entry.value(QLatin1String("cmd")).toString();
|
||||
target.args = readStringList(entry.value(QLatin1String("args")));
|
||||
target.env = readEnv(entry.value(QLatin1String("env")));
|
||||
distribution.binaryTargets.append(target);
|
||||
}
|
||||
return distribution;
|
||||
}
|
||||
|
||||
AgentDistribution readCommandDistribution(const QJsonObject &object)
|
||||
{
|
||||
AgentDistribution distribution;
|
||||
distribution.kind = AgentDistributionKind::Command;
|
||||
distribution.command = object.value(QLatin1String("cmd")).toString();
|
||||
if (distribution.command.isEmpty())
|
||||
distribution.command = object.value(QLatin1String("command")).toString();
|
||||
distribution.args = readStringList(object.value(QLatin1String("args")));
|
||||
distribution.env = readEnv(object.value(QLatin1String("env")));
|
||||
return distribution;
|
||||
}
|
||||
|
||||
QString distributionProblem(const AgentDistribution &distribution)
|
||||
{
|
||||
switch (distribution.kind) {
|
||||
case AgentDistributionKind::Unknown:
|
||||
return QStringLiteral("has no supported distribution");
|
||||
case AgentDistributionKind::Npx:
|
||||
case AgentDistributionKind::Uvx:
|
||||
if (distribution.package.isEmpty())
|
||||
return QStringLiteral("has a %1 distribution without a package")
|
||||
.arg(agentDistributionName(distribution.kind));
|
||||
break;
|
||||
case AgentDistributionKind::Command:
|
||||
if (distribution.command.isEmpty())
|
||||
return QStringLiteral("has a command distribution without a cmd");
|
||||
break;
|
||||
case AgentDistributionKind::Binary:
|
||||
if (distribution.binaryTargets.isEmpty())
|
||||
return QStringLiteral("has a binary distribution without platform targets");
|
||||
break;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
AgentDistribution readDistribution(const QJsonObject &object)
|
||||
{
|
||||
if (object.contains(QLatin1String("npx")))
|
||||
return readRunnerDistribution(
|
||||
object.value(QLatin1String("npx")).toObject(), AgentDistributionKind::Npx);
|
||||
|
||||
if (object.contains(QLatin1String("uvx")))
|
||||
return readRunnerDistribution(
|
||||
object.value(QLatin1String("uvx")).toObject(), AgentDistributionKind::Uvx);
|
||||
|
||||
if (object.contains(QLatin1String("command")))
|
||||
return readCommandDistribution(object.value(QLatin1String("command")).toObject());
|
||||
|
||||
if (object.contains(QLatin1String("binary")))
|
||||
return readBinaryDistribution(object.value(QLatin1String("binary")).toObject());
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace AgentRegistryParser {
|
||||
|
||||
AgentParseResult parse(const QJsonValue &root, AgentSource source, const QString &origin)
|
||||
{
|
||||
AgentParseResult result;
|
||||
|
||||
QJsonArray entries;
|
||||
if (root.isArray()) {
|
||||
entries = root.toArray();
|
||||
} else if (root.isObject()) {
|
||||
const QJsonObject object = root.toObject();
|
||||
if (object.contains(QLatin1String("agents"))) {
|
||||
const QJsonValue agents = object.value(QLatin1String("agents"));
|
||||
if (!agents.isArray()) {
|
||||
result.warnings.append(QStringLiteral("%1: 'agents' is not an array").arg(origin));
|
||||
return result;
|
||||
}
|
||||
entries = agents.toArray();
|
||||
} else if (object.contains(QLatin1String("id")))
|
||||
entries.append(root);
|
||||
else
|
||||
result.warnings.append(QStringLiteral("%1: no agents found").arg(origin));
|
||||
} else {
|
||||
result.warnings.append(QStringLiteral("%1: not a JSON object or array").arg(origin));
|
||||
return result;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for (const QJsonValue &entry : std::as_const(entries)) {
|
||||
const int position = index++;
|
||||
if (!entry.isObject()) {
|
||||
result.warnings.append(
|
||||
QStringLiteral("%1: entry %2 is not an object").arg(origin).arg(position));
|
||||
continue;
|
||||
}
|
||||
|
||||
const QJsonObject object = entry.toObject();
|
||||
AgentDefinition agent;
|
||||
agent.id = object.value(QLatin1String("id")).toString();
|
||||
if (agent.id.isEmpty()) {
|
||||
result.warnings.append(
|
||||
QStringLiteral("%1: entry %2 has no id").arg(origin).arg(position));
|
||||
continue;
|
||||
}
|
||||
|
||||
agent.name = object.value(QLatin1String("name")).toString();
|
||||
if (agent.name.isEmpty())
|
||||
agent.name = agent.id;
|
||||
|
||||
agent.version = object.value(QLatin1String("version")).toString();
|
||||
agent.description = object.value(QLatin1String("description")).toString();
|
||||
agent.icon = object.value(QLatin1String("icon")).toString();
|
||||
agent.repository = object.value(QLatin1String("repository")).toString();
|
||||
agent.website = object.value(QLatin1String("website")).toString();
|
||||
agent.license = object.value(QLatin1String("license")).toString();
|
||||
agent.authors = readStringList(object.value(QLatin1String("authors")));
|
||||
agent.distribution = readDistribution(
|
||||
object.value(QLatin1String("distribution")).toObject());
|
||||
agent.source = source;
|
||||
agent.origin = origin;
|
||||
|
||||
const QString problem = distributionProblem(agent.distribution);
|
||||
if (!problem.isEmpty()) {
|
||||
result.warnings.append(
|
||||
QStringLiteral("%1: agent '%2' %3").arg(origin, agent.id, problem));
|
||||
}
|
||||
|
||||
result.agents.append(agent);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
AgentParseResult parse(const QByteArray &json, AgentSource source, const QString &origin)
|
||||
{
|
||||
QJsonParseError error;
|
||||
const QJsonDocument document = QJsonDocument::fromJson(json, &error);
|
||||
if (document.isNull()) {
|
||||
AgentParseResult result;
|
||||
result.warnings.append(QStringLiteral("%1: %2").arg(origin, error.errorString()));
|
||||
return result;
|
||||
}
|
||||
|
||||
if (document.isArray())
|
||||
return parse(QJsonValue(document.array()), source, origin);
|
||||
return parse(QJsonValue(document.object()), source, origin);
|
||||
}
|
||||
|
||||
} // namespace AgentRegistryParser
|
||||
|
||||
} // namespace QodeAssist::Acp
|
||||
Reference in New Issue
Block a user