repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources15d ago| #1 | import { readFile } from 'node:fs/promises'; |
| #2 | import { createUmi } from '@metaplex-foundation/umi-bundle-defaults'; |
| #3 | import { |
| #4 | keypairIdentity, |
| #5 | publicKey, |
| #6 | type PublicKey, |
| #7 | type Umi, |
| #8 | } from '@metaplex-foundation/umi'; |
| #9 | import { |
| #10 | isAgentApiError, |
| #11 | isAgentApiNetworkError, |
| #12 | isAgentValidationError, |
| #13 | mintAndSubmitAgent, |
| #14 | mplAgentIdentity, |
| #15 | safeFetchAgentIdentityV1FromSeeds, |
| #16 | } from '@metaplex-foundation/mpl-agent-registry'; |
| #17 | import { |
| #18 | fetchAsset, |
| #19 | findAssetSignerPda, |
| #20 | mplCore, |
| #21 | } from '@metaplex-foundation/mpl-core'; |
| #22 | import bs58 from 'bs58'; |
| #23 | |
| #24 | export type AgentNetwork = |
| #25 | | 'solana-mainnet' |
| #26 | | 'solana-devnet' |
| #27 | | 'localnet' |
| #28 | | 'eclipse-mainnet' |
| #29 | | 'sonic-mainnet' |
| #30 | | 'sonic-devnet' |
| #31 | | 'fogo-mainnet' |
| #32 | | 'fogo-testnet'; |
| #33 | |
| #34 | export interface AgentServiceInput { |
| #35 | name: string; |
| #36 | endpoint: string; |
| #37 | version?: string; |
| #38 | skills?: string[]; |
| #39 | domains?: string[]; |
| #40 | } |
| #41 | |
| #42 | export interface AgentRegistrationInput { |
| #43 | agentId: string; |
| #44 | agentRegistry: string; |
| #45 | } |
| #46 | |
| #47 | export interface AgentMetadataInput { |
| #48 | type: 'agent' | 'https://eips.ethereum.org/EIPS/eip-8004#registration-v1'; |
| #49 | name: string; |
| #50 | description: string; |
| #51 | image?: string; |
| #52 | services: AgentServiceInput[]; |
| #53 | registrations: AgentRegistrationInput[]; |
| #54 | supportedTrust: string[]; |
| #55 | active?: boolean; |
| #56 | x402Support?: boolean; |
| #57 | } |
| #58 | |
| #59 | export interface MintRegisteredAgentOptions { |
| #60 | keypairPath: string; |
| #61 | rpcUrl?: string; |
| #62 | network?: AgentNetwork; |
| #63 | name: string; |
| #64 | uri: string; |
| #65 | description: string; |
| #66 | image?: string; |
| #67 | services?: AgentServiceInput[]; |
| #68 | registrations?: AgentRegistrationInput[]; |
| #69 | supportedTrust?: string[]; |
| #70 | x402Support?: boolean; |
| #71 | baseUrl?: string; |
| #72 | } |
| #73 | |
| #74 | export interface MintRegisteredAgentResult { |
| #75 | assetAddress: string; |
| #76 | signature: string; |
| #77 | owner: string; |
| #78 | agentWallet: string; |
| #79 | network: AgentNetwork; |
| #80 | rpcUrl: string; |
| #81 | explorerUrl: string; |
| #82 | registered: boolean; |
| #83 | registrationUri?: string; |
| #84 | } |
| #85 | |
| #86 | export interface ReadRegisteredAgentResult { |
| #87 | assetAddress: string; |
| #88 | registered: boolean; |
| #89 | registrationUri?: string; |
| #90 | agentWallet: string; |
| #91 | owner?: string; |
| #92 | name?: string; |
| #93 | uri?: string; |
| #94 | lifecycleChecks?: unknown; |
| #95 | } |
| #96 | |
| #97 | export function defaultRpcForNetwork(network: AgentNetwork): string { |
| #98 | if (network === 'solana-devnet') return 'https://api.devnet.solana.com'; |
| #99 | if (network === 'solana-mainnet') return 'https://api.mainnet-beta.solana.com'; |
| #100 | return 'https://api.mainnet-beta.solana.com'; |
| #101 | } |
| #102 | |
| #103 | export function inferNetwork(value: string | undefined): AgentNetwork { |
| #104 | if (!value || value === 'mainnet') return 'solana-mainnet'; |
| #105 | if (value === 'devnet') return 'solana-devnet'; |
| #106 | return value as AgentNetwork; |
| #107 | } |
| #108 | |
| #109 | export function buildAgentMetadata(input: { |
| #110 | name: string; |
| #111 | description: string; |
| #112 | image?: string; |
| #113 | services?: AgentServiceInput[]; |
| #114 | registrations?: AgentRegistrationInput[]; |
| #115 | supportedTrust?: string[]; |
| #116 | x402Support?: boolean; |
| #117 | }): AgentMetadataInput { |
| #118 | return { |
| #119 | type: 'https://eips.ethereum.org/EIPS/eip-8004#registration-v1', |
| #120 | name: input.name, |
| #121 | description: input.description, |
| #122 | image: input.image, |
| #123 | services: input.services ?? [], |
| #124 | active: true, |
| #125 | registrations: input.registrations ?? [], |
| #126 | supportedTrust: input.supportedTrust ?? ['reputation', 'crypto-economic'], |
| #127 | x402Support: input.x402Support ?? true, |
| #128 | }; |
| #129 | } |
| #130 | |
| #131 | export async function createAgentUmi(opts: { |
| #132 | keypairPath?: string; |
| #133 | rpcUrl?: string; |
| #134 | network?: AgentNetwork; |
| #135 | }): Promise<{ umi: Umi; owner?: PublicKey; rpcUrl: string }> { |
| #136 | const network = opts.network ?? 'solana-devnet'; |
| #137 | const rpcUrl = opts.rpcUrl ?? defaultRpcForNetwork(network); |
| #138 | const umi = createUmi(rpcUrl).use(mplCore()).use(mplAgentIdentity()); |
| #139 | |
| #140 | if (!opts.keypairPath) return { umi, rpcUrl }; |
| #141 | |
| #142 | const secretBytes = await readSecretKey(opts.keypairPath); |
| #143 | const keypair = umi.eddsa.createKeypairFromSecretKey(secretBytes); |
| #144 | umi.use(keypairIdentity(keypair)); |
| #145 | return { umi, owner: keypair.publicKey, rpcUrl }; |
| #146 | } |
| #147 | |
| #148 | export async function mintRegisteredAgent( |
| #149 | opts: MintRegisteredAgentOptions, |
| #150 | ): Promise<MintRegisteredAgentResult> { |
| #151 | const network = opts.network ?? 'solana-devnet'; |
| #152 | const { umi, owner, rpcUrl } = await createAgentUmi({ |
| #153 | keypairPath: opts.keypairPath, |
| #154 | rpcUrl: opts.rpcUrl, |
| #155 | network, |
| #156 | }); |
| #157 | |
| #158 | if (!owner) throw new Error('A funded keypair is required to mint an agent'); |
| #159 | |
| #160 | const result = await mintAndSubmitAgent( |
| #161 | umi, |
| #162 | opts.baseUrl ? { baseUrl: opts.baseUrl } : {}, |
| #163 | { |
| #164 | wallet: owner, |
| #165 | network, |
| #166 | name: opts.name, |
| #167 | uri: opts.uri, |
| #168 | agentMetadata: buildAgentMetadata({ |
| #169 | name: opts.name, |
| #170 | description: opts.description, |
| #171 | image: opts.image, |
| #172 | services: opts.services, |
| #173 | registrations: opts.registrations, |
| #174 | supportedTrust: opts.supportedTrust, |
| #175 | x402Support: opts.x402Support, |
| #176 | }), |
| #177 | }, |
| #178 | ); |
| #179 | |
| #180 | const assetAddress = result.assetAddress.toString(); |
| #181 | const assetPublicKey = publicKey(assetAddress); |
| #182 | const agentWallet = findAssetSignerPda(umi, { asset: assetPublicKey })[0].toString(); |
| #183 | const read = await readRegisteredAgent({ |
| #184 | assetAddress, |
| #185 | rpcUrl, |
| #186 | network, |
| #187 | }).catch(() => null); |
| #188 | |
| #189 | return { |
| #190 | assetAddress, |
| #191 | signature: formatSignature(result.signature), |
| #192 | owner: owner.toString(), |
| #193 | agentWallet, |
| #194 | network, |
| #195 | rpcUrl, |
| #196 | explorerUrl: explorerUrl(assetAddress, network), |
| #197 | registered: read?.registered ?? false, |
| #198 | registrationUri: read?.registrationUri, |
| #199 | }; |
| #200 | } |
| #201 | |
| #202 | export async function readRegisteredAgent(opts: { |
| #203 | assetAddress: string; |
| #204 | rpcUrl?: string; |
| #205 | network?: AgentNetwork; |
| #206 | }): Promise<ReadRegisteredAgentResult> { |
| #207 | const network = opts.network ?? 'solana-devnet'; |
| #208 | const { umi } = await createAgentUmi({ rpcUrl: opts.rpcUrl, network }); |
| #209 | const asset = publicKey(opts.assetAddress); |
| #210 | const identity = await safeFetchAgentIdentityV1FromSeeds(umi, { asset }); |
| #211 | const assetData = await fetchAsset(umi, asset).catch(() => null); |
| #212 | const agentIdentity = assetData?.agentIdentities?.[0]; |
| #213 | |
| #214 | return { |
| #215 | assetAddress: opts.assetAddress, |
| #216 | registered: identity !== null || agentIdentity !== undefined, |
| #217 | registrationUri: agentIdentity?.uri, |
| #218 | agentWallet: findAssetSignerPda(umi, { asset })[0].toString(), |
| #219 | owner: assetData?.owner?.toString(), |
| #220 | name: assetData?.name, |
| #221 | uri: assetData?.uri, |
| #222 | lifecycleChecks: agentIdentity?.lifecycleChecks, |
| #223 | }; |
| #224 | } |
| #225 | |
| #226 | export function formatAgentError(err: unknown): string { |
| #227 | if (isAgentValidationError(err)) { |
| #228 | return `Validation error${err.field ? ` on ${err.field}` : ''}: ${err.message}`; |
| #229 | } |
| #230 | if (isAgentApiNetworkError(err)) { |
| #231 | return `Metaplex API network error: ${err.message}`; |
| #232 | } |
| #233 | if (isAgentApiError(err)) { |
| #234 | return `Metaplex API error ${err.statusCode}: ${err.message}\n${String(err.responseBody ?? '')}`; |
| #235 | } |
| #236 | return err instanceof Error ? err.message : String(err); |
| #237 | } |
| #238 | |
| #239 | function explorerUrl(assetAddress: string, network: AgentNetwork): string { |
| #240 | const cluster = network === 'solana-devnet' ? '?cluster=devnet' : ''; |
| #241 | return `https://explorer.solana.com/address/${assetAddress}${cluster}`; |
| #242 | } |
| #243 | |
| #244 | function formatSignature(signature: unknown): string { |
| #245 | if (typeof signature === 'string') return signature; |
| #246 | if (signature instanceof Uint8Array) return bs58.encode(signature); |
| #247 | if (Array.isArray(signature)) return bs58.encode(Uint8Array.from(signature as number[])); |
| #248 | return String(signature); |
| #249 | } |
| #250 | |
| #251 | async function readSecretKey(path: string): Promise<Uint8Array> { |
| #252 | const raw = (await readFile(expandHome(path), 'utf8')).trim(); |
| #253 | if (raw.startsWith('[')) return Uint8Array.from(JSON.parse(raw) as number[]); |
| #254 | return bs58.decode(raw); |
| #255 | } |
| #256 | |
| #257 | function expandHome(path: string): string { |
| #258 | if (!path.startsWith('~/')) return path; |
| #259 | return `${process.env['HOME'] ?? ''}${path.slice(1)}`; |
| #260 | } |
| #261 |