UNPKG

@silvana-one/coordination

Version:

Silvana Coordination Client

321 lines 12.9 kB
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