@silvana-one/coordination
Version:
Silvana Coordination Client
321 lines • 12.9 kB
JavaScript
import { Transaction } from "@mysten/sui/transactions";
import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils";
import { fetchSuiDynamicField, fetchSuiDynamicFieldsList, } from "./fetch.js";
export class AgentRegistry {
constructor(params) {
this.registry = params.registry;
}
static createAgentRegistry(params) {
console.log("Creating agent registry", params.name);
const transaction = new Transaction();
transaction.moveCall({
target: `@silvana/agent::registry::create_registry`,
arguments: [transaction.pure.string(params.name)],
});
return transaction;
}
createDeveloper(params) {
const { name, github, image, description, site } = params;
const tx = new Transaction();
tx.moveCall({
target: `@silvana/agent::registry::add_developer`,
arguments: [
tx.object(this.registry),
tx.pure.string(name),
tx.pure.string(github),
tx.pure.option("string", image ?? null),
tx.pure.option("string", description ?? null),
tx.pure.option("string", site ?? null),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
updateDeveloper(params) {
const { name, github, image, description, site } = params;
const tx = new Transaction();
tx.moveCall({
target: `@silvana/agent::registry::update_developer`,
arguments: [
tx.object(this.registry),
tx.pure.string(name),
tx.pure.string(github),
tx.pure.option("string", image ?? null),
tx.pure.option("string", description ?? null),
tx.pure.option("string", site ?? null),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
removeDeveloper(params) {
const { name, agentNames } = params;
const tx = new Transaction();
tx.moveCall({
target: `@silvana/agent::registry::remove_developer`,
arguments: [
tx.object(this.registry),
tx.pure.string(name),
tx.pure.vector("string", agentNames),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
createAgent(params) {
const { developer, name, image, description, site, docker_image, docker_sha256, min_memory_gb, min_cpu_cores, supports_tee, chains, } = params;
const tx = new Transaction();
tx.moveCall({
target: `@silvana/agent::registry::add_agent`,
arguments: [
tx.object(this.registry),
tx.pure.string(developer),
tx.pure.string(name),
tx.pure.option("string", image ?? null),
tx.pure.option("string", description ?? null),
tx.pure.option("string", site ?? null),
tx.pure.string(docker_image),
tx.pure.option("string", docker_sha256 ?? null),
tx.pure.u16(min_memory_gb),
tx.pure.u16(min_cpu_cores),
tx.pure.bool(supports_tee),
tx.pure.vector("string", chains),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
updateAgent(params) {
const { developer, name, image, description, site, docker_image, docker_sha256, min_memory_gb, min_cpu_cores, supports_tee, chains, } = params;
const tx = new Transaction();
tx.moveCall({
target: `@silvana/agent::registry::update_agent`,
arguments: [
tx.object(this.registry),
tx.pure.string(developer),
tx.pure.string(name),
tx.pure.option("string", image ?? null),
tx.pure.option("string", description ?? null),
tx.pure.option("string", site ?? null),
tx.pure.string(docker_image),
tx.pure.option("string", docker_sha256 ?? null),
tx.pure.u16(min_memory_gb),
tx.pure.u16(min_cpu_cores),
tx.pure.bool(supports_tee),
tx.pure.vector("string", chains),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
removeAgent(params) {
const { developer, agent } = params;
const tx = new Transaction();
tx.moveCall({
target: `@silvana/agent::registry::remove_agent`,
arguments: [
tx.object(this.registry),
tx.pure.string(developer),
tx.pure.string(agent),
tx.object(SUI_CLOCK_OBJECT_ID),
],
});
return tx;
}
async getDeveloper(params) {
const developerObject = await fetchSuiDynamicField({
objectID: this.registry,
fieldName: "developers",
type: "0x1::string::String",
key: params.name,
});
if (!developerObject) {
return undefined;
}
let agents = [];
const agentsObject = developerObject?.agents?.fields?.id?.id;
if (agentsObject) {
const agentsList = await fetchSuiDynamicFieldsList(agentsObject);
const agentsArray = agentsList?.data;
if (Array.isArray(agentsArray)) {
agents = agentsArray
.map((agent) => agent?.name?.value)
.filter((agent) => agent !== undefined && typeof agent === "string");
}
}
const developer = {
id: developerObject?.id?.id,
name: developerObject.name,
github: developerObject.github,
image: developerObject?.image ?? undefined,
description: developerObject?.description ?? undefined,
site: developerObject?.site ?? undefined,
owner: developerObject.owner,
agents,
createdAt: Number(developerObject.created_at),
updatedAt: Number(developerObject.updated_at),
version: Number(developerObject.version),
};
if (!developer.id ||
!developer.name ||
!developer.github ||
!developer.owner ||
!developer.createdAt ||
!developer.updatedAt) {
return undefined;
}
return developer;
}
async getDeveloperNames(params) {
const developerObject = await fetchSuiDynamicField({
objectID: this.registry,
fieldName: "developers_index",
type: "address",
key: params.developerAddress,
});
if (!developerObject) {
return undefined;
}
const developer = {
id: developerObject?.id?.id,
developer_address: developerObject.developer,
names: developerObject.names,
version: Number(developerObject.version),
};
if (!developer.id || !developer.developer_address || !developer.names) {
return undefined;
}
return developer;
}
async getAgent(params) {
const developerObject = await fetchSuiDynamicField({
objectID: this.registry,
fieldName: "developers",
type: "0x1::string::String",
key: params.developer,
});
const id = developerObject?.agents?.fields?.id?.id;
if (!id) {
return undefined;
}
const agentObject = await fetchSuiDynamicField({
parentID: id,
fieldName: "agents",
type: "0x1::string::String",
key: params.agent,
});
if (!agentObject) {
return undefined;
}
const agent = {
id: agentObject?.id?.id,
name: agentObject.name,
image: agentObject?.image ?? undefined,
description: agentObject?.description ?? undefined,
site: agentObject?.site ?? undefined,
dockerImage: agentObject.docker_image,
dockerSha256: agentObject?.docker_sha256 ?? undefined,
minMemoryGb: Number(agentObject.min_memory_gb),
minCpuCores: Number(agentObject.min_cpu_cores),
supportsTEE: Boolean(agentObject.supports_tee),
createdAt: Number(agentObject.created_at),
updatedAt: Number(agentObject.updated_at),
version: Number(agentObject.version),
};
if (!agent.id ||
!agent.name ||
!agent.dockerImage ||
!agent.minMemoryGb ||
!agent.minCpuCores ||
!agent.createdAt ||
!agent.updatedAt) {
return undefined;
}
return agent;
}
static async getDockerImageDetails(params) {
try {
const { dockerImage } = params;
// Parse image_source to extract repository and tag
const colonPos = dockerImage.lastIndexOf(":");
const repository = colonPos !== -1 ? dockerImage.slice(0, colonPos) : dockerImage;
const tag = colonPos !== -1 ? dockerImage.slice(colonPos + 1) : "latest";
// 1. Get token
const tokenResponse = await fetch("https://auth.docker.io/token?" +
new URLSearchParams({
service: "registry.docker.io",
scope: `repository:${repository}:pull`,
}));
if (!tokenResponse.ok) {
return undefined;
}
const tokenData = await tokenResponse.json();
const token = tokenData.token;
if (!token) {
return undefined;
}
// 2. Fetch manifest/index
const manifestResponse = await fetch(`https://registry-1.docker.io/v2/${repository}/manifests/${tag}`, {
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/vnd.docker.distribution.manifest.list.v2+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.v2+json",
},
});
if (!manifestResponse.ok) {
return undefined;
}
const contentType = manifestResponse.headers.get("content-type") || "";
// Extract the digest from the response headers
let digest = manifestResponse.headers.get("docker-content-digest") || "";
let manifest;
if (contentType.includes("index") || contentType.includes("list")) {
// This is a manifest index (multi-platform)
const idx = await manifestResponse.json();
// Pick amd64/linux manifest
const platformManifest = idx.manifests?.find((m) => m.platform?.architecture === "amd64" && m.platform?.os === "linux");
if (!platformManifest) {
return undefined;
}
const platformDigest = platformManifest.digest;
// 3. Fetch the actual manifest
const actualManifestResponse = await fetch(`https://registry-1.docker.io/v2/${repository}/manifests/${platformDigest}`, {
headers: {
Authorization: `Bearer ${token}`,
Accept: "application/vnd.docker.distribution.manifest.v2+json",
},
});
if (!actualManifestResponse.ok) {
return undefined;
}
manifest = await actualManifestResponse.json();
// Update digest from the actual manifest response
const actualDigest = actualManifestResponse.headers.get("docker-content-digest");
if (actualDigest) {
digest = actualDigest;
}
}
else {
// This is already a direct manifest (single platform)
manifest = await manifestResponse.json();
}
if (!manifest?.layers || !Array.isArray(manifest.layers)) {
return undefined;
}
const numberOfLayers = manifest.layers.length;
// Remove the "sha256:" prefix if present
const sha256 = digest.startsWith("sha256:") ? digest.slice(7) : digest;
if (!sha256) {
return undefined;
}
return {
sha256,
numberOfLayers,
};
}
catch (error) {
console.error("Error fetching Docker image details:", error);
return undefined;
}
}
}
//# sourceMappingURL=agent.js.map