UNPKG

@silvana-one/coordination

Version:

Silvana Coordination Client

543 lines 21.4 kB
import { Transaction } from "@mysten/sui/transactions"; import { SUI_CLOCK_OBJECT_ID } from "@mysten/sui/utils"; import { fetchSuiDynamicField, fetchSuiDynamicFieldsList } from "./fetch.js"; import { silvanaRegistryPackage } from "./package.js"; export class AgentRegistry { constructor(params) { this.registry = params.registry; } static createAgentRegistry(params) { const { name, transaction } = params; console.log("Creating agent registry", name); const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::create_registry`, arguments: [tx.pure.string(name)], }); return tx; } createDeveloper(params) { const { name, developerOwner, github, image, description, site, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::add_developer`, arguments: [ tx.object(this.registry), tx.pure.address(developerOwner), 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, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::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, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::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, chains, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::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.vector("string", chains), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } updateAgent(params) { const { developer, name, image, description, site, chains, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::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.vector("string", chains), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } removeAgent(params) { const { developer, agent, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::remove_agent`, arguments: [ tx.object(this.registry), tx.pure.string(developer), tx.pure.string(agent), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } addAgentMethod(params) { const { developer, agent, method, dockerImage, dockerSha256, minMemoryGb, minCpuCores, requiresTee, transaction, } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::add_method`, arguments: [ tx.object(this.registry), tx.pure.string(developer), tx.pure.string(agent), tx.pure.string(method), tx.pure.string(dockerImage), tx.pure.option("string", dockerSha256 ?? null), tx.pure.u16(minMemoryGb), tx.pure.u16(minCpuCores), tx.pure.bool(requiresTee), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } updateAgentMethod(params) { const { developer, agent, method, dockerImage, dockerSha256, minMemoryGb, minCpuCores, requiresTee, transaction, } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::update_method`, arguments: [ tx.object(this.registry), tx.pure.string(developer), tx.pure.string(agent), tx.pure.string(method), tx.pure.string(dockerImage), tx.pure.option("string", dockerSha256 ?? null), tx.pure.u16(minMemoryGb), tx.pure.u16(minCpuCores), tx.pure.bool(requiresTee), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } removeAgentMethod(params) { const { developer, agent, method, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::remove_method`, arguments: [ tx.object(this.registry), tx.pure.string(developer), tx.pure.string(agent), tx.pure.string(method), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } addMethodToApp(params) { const { appName, methodName, description, developerName, agentName, agentMethod, transaction, } = params; const tx = transaction ?? new Transaction(); // Create the app method using app_method::new const appMethod = tx.moveCall({ target: `${silvanaRegistryPackage}::app_method::new`, arguments: [ tx.pure.option("string", description ?? null), tx.pure.string(developerName), tx.pure.string(agentName), tx.pure.string(agentMethod), ], }); // Add the method to the app tx.moveCall({ target: `${silvanaRegistryPackage}::registry::add_method_to_app`, arguments: [ tx.object(this.registry), tx.pure.string(appName), tx.pure.string(methodName), appMethod, ], }); return tx; } addMetadata(params) { const { appInstanceId, key, value, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::app_instance::add_metadata`, arguments: [ tx.object(appInstanceId), tx.pure.string(key), tx.pure.string(value), ], }); return tx; } setDefaultMethod(params) { const { developer, agent, method, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::set_default_method`, arguments: [ tx.object(this.registry), tx.pure.string(developer), tx.pure.string(agent), tx.pure.string(method), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } removeDefaultMethod(params) { const { developer, agent, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::remove_default_method`, 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; } // Parse methods from VecMap structure const methods = {}; const methodsData = agentObject?.methods?.fields?.contents; if (methodsData && Array.isArray(methodsData)) { for (const entry of methodsData) { const key = entry?.fields?.key; const value = entry?.fields?.value; if (key && value) { methods[key] = { dockerImage: value.docker_image, dockerSha256: value.docker_sha256 ?? undefined, minMemoryGb: Number(value.min_memory_gb), minCpuCores: Number(value.min_cpu_cores), requiresTee: Boolean(value.requires_tee), }; } } } // Parse default method if it exists let defaultMethod; const defaultMethodData = agentObject?.default_method; if (defaultMethodData && typeof defaultMethodData === "object" && !Array.isArray(defaultMethodData)) { defaultMethod = { dockerImage: defaultMethodData.docker_image, dockerSha256: defaultMethodData.docker_sha256 ?? undefined, minMemoryGb: Number(defaultMethodData.min_memory_gb), minCpuCores: Number(defaultMethodData.min_cpu_cores), requiresTee: Boolean(defaultMethodData.requires_tee), }; } const agent = { id: agentObject?.id?.id, name: agentObject.name, image: agentObject?.image ?? undefined, description: agentObject?.description ?? undefined, site: agentObject?.site ?? undefined, chains: agentObject?.chains ?? [], methods, defaultMethod, createdAt: Number(agentObject.created_at), updatedAt: Number(agentObject.updated_at), version: Number(agentObject.version), }; // Only check for essential fields if (!agent.id || !agent.name) { return undefined; } return agent; } async getApp(params) { const appObject = await fetchSuiDynamicField({ objectID: this.registry, fieldName: "apps", type: "0x1::string::String", key: params.name, }); if (!appObject) { return undefined; } // Parse methods from VecMap structure const methods = {}; const methodsData = appObject?.methods?.fields?.contents; if (methodsData && Array.isArray(methodsData)) { for (const entry of methodsData) { const key = entry?.fields?.key; const value = entry?.fields?.value?.fields; if (key && value) { methods[key] = { description: value.description ?? undefined, developer: value.developer, agent: value.agent, agentMethod: value.agent_method, }; } } } // Parse instances from VecSet structure const instances = []; const instancesData = appObject?.instances?.fields?.contents; if (instancesData && Array.isArray(instancesData)) { for (const instance of instancesData) { if (instance?.fields?.key) { instances.push(instance.fields.key); } } } const app = { id: appObject?.id?.id, name: appObject.name, description: appObject?.description ?? undefined, methods, owner: appObject.owner, createdAt: Number(appObject.created_at), updatedAt: Number(appObject.updated_at), version: Number(appObject.version), instances, }; // Check for essential fields if (!app.id || !app.name || !app.owner) { return undefined; } return app; } createApp(params) { const { name, owner, description, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::add_app`, arguments: [ tx.object(this.registry), tx.pure.string(name), tx.pure.address(owner), tx.pure.option("string", description ?? null), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } removeApp(params) { const { name, transaction } = params; const tx = transaction ?? new Transaction(); tx.moveCall({ target: `${silvanaRegistryPackage}::registry::remove_app`, arguments: [ tx.object(this.registry), tx.pure.string(name), tx.object(SUI_CLOCK_OBJECT_ID), ], }); return tx; } 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