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 sources16d ago| #1 | /** |
| #2 | * clawd — Agent Registry Screen |
| #3 | * |
| #4 | * Browse Solana Clawd agents and surface the free/gasless x402 registry path. |
| #5 | */ |
| #6 | |
| #7 | import chalk from 'chalk'; |
| #8 | import { existsSync, readFileSync } from 'node:fs'; |
| #9 | import { resolve } from 'node:path'; |
| #10 | |
| #11 | interface CatalogAgent { |
| #12 | identifier: string; |
| #13 | title: string; |
| #14 | description: string; |
| #15 | tags?: string[]; |
| #16 | category?: string; |
| #17 | author?: string; |
| #18 | capabilities?: string[]; |
| #19 | metaplexSkills?: string[]; |
| #20 | } |
| #21 | |
| #22 | interface AgentCatalog { |
| #23 | generatedAt?: string; |
| #24 | stats?: { |
| #25 | totalAgents?: number; |
| #26 | totalOneShots?: number; |
| #27 | totalFeatured?: number; |
| #28 | metaplexEnabledAgents?: number; |
| #29 | }; |
| #30 | agents?: CatalogAgent[]; |
| #31 | } |
| #32 | |
| #33 | type CommandMode = 'official' | 'preset' | 'custom' | 'registry'; |
| #34 | |
| #35 | const HUB_URL = 'https://x402.wtf/agents'; |
| #36 | const API_BASE = 'https://x402.wtf'; |
| #37 | |
| #38 | const FALLBACK_AGENTS: CatalogAgent[] = [ |
| #39 | { |
| #40 | identifier: 'solana-clawd-wallet-guardian', |
| #41 | title: 'Clawd Wallet Guardian', |
| #42 | description: 'Wallet safety, biometric approval, and prompt-injection defense for paid Solana agents.', |
| #43 | category: 'payments', |
| #44 | author: 'solana-clawd', |
| #45 | tags: ['solana', 'clawd', 'wallet', 'x402'], |
| #46 | capabilities: ['free-registry', 'gasless-mint', 'x402-approval'], |
| #47 | metaplexSkills: ['agent-registry', 'core'], |
| #48 | }, |
| #49 | { |
| #50 | identifier: 'solana-openclawd-orchestrator', |
| #51 | title: 'Solana OpenClawd Orchestrator', |
| #52 | description: 'Coordinates Solana Clawd agents across tools, permissions, memory, and runtime surfaces.', |
| #53 | category: 'infrastructure', |
| #54 | author: 'solana-clawd', |
| #55 | tags: ['solana', 'clawd', 'orchestration'], |
| #56 | capabilities: ['agent-routing', 'permission-control'], |
| #57 | metaplexSkills: ['agent-registry'], |
| #58 | }, |
| #59 | { |
| #60 | identifier: 'solana-pumpfun-bot', |
| #61 | title: 'Solana PumpFun/PumpSwap Copy Trading Bot', |
| #62 | description: 'High-performance copy trading agent for PumpFun, PumpSwap, and Raydium flows.', |
| #63 | category: 'trading', |
| #64 | author: 'solana-clawd', |
| #65 | tags: ['solana', 'pumpfun', 'trading-bot'], |
| #66 | capabilities: ['market-monitoring', 'trade-routing'], |
| #67 | metaplexSkills: ['agent-registry'], |
| #68 | }, |
| #69 | ]; |
| #70 | |
| #71 | function visible(s: string): number { |
| #72 | // eslint-disable-next-line no-control-regex |
| #73 | return s.replace(/\x1b\[[0-9;]*m/g, '').length; |
| #74 | } |
| #75 | |
| #76 | function pad(s: string, width: number): string { |
| #77 | return s + ' '.repeat(Math.max(0, width - visible(s))); |
| #78 | } |
| #79 | |
| #80 | function clip(s: string, width: number): string { |
| #81 | if (visible(s) <= width) return s; |
| #82 | // Color is not important on clipped text; prefer stable layout. |
| #83 | // eslint-disable-next-line no-control-regex |
| #84 | return s.replace(/\x1b\[[0-9;]*m/g, '').slice(0, Math.max(0, width - 1)) + '…'; |
| #85 | } |
| #86 | |
| #87 | function boxed(content: string, width: number): string { |
| #88 | const inner = width - 2; |
| #89 | return chalk.cyan('║') + pad(clip(content, inner), inner) + chalk.cyan('║'); |
| #90 | } |
| #91 | |
| #92 | function loadCatalog(): { catalog: AgentCatalog; source: string } { |
| #93 | const candidates = [ |
| #94 | resolve(process.cwd(), 'agents', 'agents-catalog.json'), |
| #95 | resolve(process.cwd(), '..', 'agents', 'agents-catalog.json'), |
| #96 | resolve(process.cwd(), 'agents-catalog.json'), |
| #97 | ]; |
| #98 | const path = candidates.find((candidate) => existsSync(candidate)); |
| #99 | |
| #100 | if (!path) { |
| #101 | return { |
| #102 | catalog: { stats: { totalAgents: FALLBACK_AGENTS.length, metaplexEnabledAgents: 2 }, agents: FALLBACK_AGENTS }, |
| #103 | source: 'fallback', |
| #104 | }; |
| #105 | } |
| #106 | |
| #107 | try { |
| #108 | return { catalog: JSON.parse(readFileSync(path, 'utf8')) as AgentCatalog, source: path }; |
| #109 | } catch { |
| #110 | return { |
| #111 | catalog: { stats: { totalAgents: FALLBACK_AGENTS.length, metaplexEnabledAgents: 2 }, agents: FALLBACK_AGENTS }, |
| #112 | source: 'fallback: catalog parse failed', |
| #113 | }; |
| #114 | } |
| #115 | } |
| #116 | |
| #117 | function commandLines(mode: CommandMode, agent: CatalogAgent, selected: number): string[] { |
| #118 | if (mode === 'official') { |
| #119 | return [ |
| #120 | 'clawd-agent mint-free --network devnet --owner <YOUR_SOLANA_PUBKEY> \\', |
| #121 | ` --name "${agent.title.replace(/"/g, '\\"').slice(0, 32)}" \\`, |
| #122 | ` --uri ${API_BASE}/api/agents/catalog/${agent.identifier}.json \\`, |
| #123 | ` --description "${agent.description.replace(/"/g, '\\"').slice(0, 80)}" \\`, |
| #124 | ` --service MCP=${API_BASE}/api/agents/catalog/${agent.identifier}.json`, |
| #125 | ]; |
| #126 | } |
| #127 | |
| #128 | if (mode === 'custom') { |
| #129 | return [ |
| #130 | `curl -X POST ${API_BASE}/api/mint/agent/custom \\`, |
| #131 | ` -H 'Content-Type: application/json' \\`, |
| #132 | ` -d '{"name":"My Clawd Agent","metadataUri":"https://example.com/agent.json","ownerPubkey":"<YOUR_SOLANA_PUBKEY>","network":"devnet"}'`, |
| #133 | ]; |
| #134 | } |
| #135 | |
| #136 | if (mode === 'registry') { |
| #137 | return [ |
| #138 | `curl ${API_BASE}/api/agents/catalog/${agent.identifier}.json | jq .`, |
| #139 | `curl ${API_BASE}/api/agents/registry | jq .`, |
| #140 | `curl ${API_BASE}/registry | jq .`, |
| #141 | ]; |
| #142 | } |
| #143 | |
| #144 | const presetId = Math.min(3, (selected % 3) + 1); |
| #145 | return [ |
| #146 | `curl -X POST ${API_BASE}/api/mint/agent \\`, |
| #147 | ` -H 'Content-Type: application/json' \\`, |
| #148 | ` -d '{"agentId":${presetId},"ownerPubkey":"<YOUR_SOLANA_PUBKEY>","network":"mainnet"}'`, |
| #149 | ]; |
| #150 | } |
| #151 | |
| #152 | function render( |
| #153 | agents: CatalogAgent[], |
| #154 | selected: number, |
| #155 | catalog: AgentCatalog, |
| #156 | source: string, |
| #157 | mode: CommandMode, |
| #158 | status: string, |
| #159 | ): void { |
| #160 | process.stdout.write('\x1b[2J\x1b[H'); |
| #161 | |
| #162 | const width = Math.max(92, Math.min(process.stdout.columns || 118, 132)); |
| #163 | const border = chalk.cyan('═'.repeat(width - 2)); |
| #164 | const current = agents[selected] ?? agents[0]!; |
| #165 | const stats = catalog.stats ?? {}; |
| #166 | const listStart = Math.max(0, Math.min(selected - 5, Math.max(0, agents.length - 10))); |
| #167 | const visibleAgents = agents.slice(listStart, listStart + 10); |
| #168 | const leftWidth = 42; |
| #169 | const rightWidth = width - leftWidth - 5; |
| #170 | const generated = catalog.generatedAt ? new Date(catalog.generatedAt).toLocaleString() : 'local'; |
| #171 | |
| #172 | process.stdout.write(chalk.cyan('╔') + border + chalk.cyan('╗') + '\n'); |
| #173 | process.stdout.write(boxed(` ${chalk.bold.cyanBright('SOLANA CLAWD AGENT REGISTRY')} ${chalk.gray('free discovery · gasless MPL Core minting · x402')}`, width) + '\n'); |
| #174 | process.stdout.write(boxed(` ${chalk.green(HUB_URL)} ${chalk.gray('mint/register agents with a Solana public key')}`, width) + '\n'); |
| #175 | process.stdout.write(chalk.cyan('╠') + border + chalk.cyan('╣') + '\n'); |
| #176 | process.stdout.write(boxed(` Catalog ${chalk.yellow(String(stats.totalAgents ?? agents.length))} Metaplex ${chalk.magenta(String(stats.metaplexEnabledAgents ?? 0))} One-shots ${chalk.cyan(String(stats.totalOneShots ?? 0))} Featured ${chalk.green(String(stats.totalFeatured ?? 0))} Generated ${chalk.gray(generated)}`, width) + '\n'); |
| #177 | process.stdout.write(boxed(` Source ${chalk.gray(source)}`, width) + '\n'); |
| #178 | process.stdout.write(chalk.cyan('╠') + border + chalk.cyan('╣') + '\n'); |
| #179 | |
| #180 | process.stdout.write(chalk.cyan('║') + ' ' + pad(chalk.bold.white('AGENTS'), leftWidth) + chalk.cyan('│') + ' ' + pad(chalk.bold.white('SELECTED IDENTITY'), rightWidth) + chalk.cyan('║') + '\n'); |
| #181 | |
| #182 | for (let i = 0; i < 10; i++) { |
| #183 | const agent = visibleAgents[i]; |
| #184 | const idx = listStart + i; |
| #185 | const marker = idx === selected ? chalk.cyanBright('>') : ' '; |
| #186 | const title = agent ? (idx === selected ? chalk.cyanBright.bold(agent.title) : chalk.white(agent.title)) : ''; |
| #187 | const left = agent ? `${marker} ${chalk.gray(String(idx + 1).padStart(3))} ${title}` : ''; |
| #188 | |
| #189 | const details = [ |
| #190 | `${chalk.cyan('id')} ${current.identifier}`, |
| #191 | `${chalk.cyan('category')} ${current.category ?? 'agent'} ${chalk.cyan('author')} ${current.author ?? 'solana-clawd'}`, |
| #192 | `${chalk.cyan('tags')} ${(current.tags ?? ['solana', 'clawd']).slice(0, 6).join(', ')}`, |
| #193 | `${chalk.cyan('metaplex')} ${(current.metaplexSkills ?? ['agent-registry']).join(', ')}`, |
| #194 | chalk.gray('─'.repeat(48)), |
| #195 | current.description, |
| #196 | `${chalk.green('free')} registry metadata, SAS, shell, cards, capabilities`, |
| #197 | `${chalk.green('gasless')} platform pays SOL fees for MPL Core mint`, |
| #198 | `${chalk.magenta('x402')} paid inference can sit behind free identity`, |
| #199 | status, |
| #200 | ]; |
| #201 | |
| #202 | process.stdout.write( |
| #203 | chalk.cyan('║') + |
| #204 | ' ' + pad(clip(left, leftWidth), leftWidth) + |
| #205 | chalk.cyan('│') + |
| #206 | ' ' + pad(clip(details[i] ?? '', rightWidth), rightWidth) + |
| #207 | chalk.cyan('║') + |
| #208 | '\n', |
| #209 | ); |
| #210 | } |
| #211 | |
| #212 | const label = mode === 'official' ? 'OFFICIAL METAPLEX AGENT MINT' : mode === 'custom' ? 'CUSTOM GASLESS MINT' : mode === 'registry' ? 'REGISTER / DISCOVER' : 'PRESET GASLESS MINT'; |
| #213 | process.stdout.write(chalk.cyan('╠') + border + chalk.cyan('╣') + '\n'); |
| #214 | process.stdout.write(boxed(` ${chalk.bold.white(label)}`, width) + '\n'); |
| #215 | for (const line of commandLines(mode, current, selected)) { |
| #216 | process.stdout.write(boxed(` ${chalk.green(line)}`, width) + '\n'); |
| #217 | } |
| #218 | process.stdout.write(chalk.cyan('╠') + border + chalk.cyan('╣') + '\n'); |
| #219 | process.stdout.write(boxed(` ${chalk.gray('[↑↓] browse [o] official mint [m] gasless [c] custom [g] registry [r] reload [b] back')}`, width) + '\n'); |
| #220 | process.stdout.write(chalk.cyan('╚') + border + chalk.cyan('╝') + '\n'); |
| #221 | } |
| #222 | |
| #223 | export async function runAgents(): Promise<void> { |
| #224 | let loaded = loadCatalog(); |
| #225 | let catalog = loaded.catalog; |
| #226 | let source = loaded.source; |
| #227 | let agents = (catalog.agents?.length ? catalog.agents : FALLBACK_AGENTS) |
| #228 | .filter((agent) => agent.identifier && agent.title) |
| #229 | .sort((a, b) => a.identifier.localeCompare(b.identifier)); |
| #230 | let selected = agents.findIndex((agent) => agent.identifier === 'solana-clawd-wallet-guardian'); |
| #231 | if (selected < 0) selected = 0; |
| #232 | let mode: CommandMode = 'official'; |
| #233 | let status = 'Ready: official mint uses Metaplex Agent Registry + local signer.'; |
| #234 | |
| #235 | const redraw = (): void => render(agents, selected, catalog, source, mode, status); |
| #236 | redraw(); |
| #237 | |
| #238 | return new Promise<void>(resolve => { |
| #239 | const enableRaw = (): void => { |
| #240 | if (process.stdin.setRawMode) process.stdin.setRawMode(true); |
| #241 | process.stdin.resume(); |
| #242 | process.stdin.setEncoding('utf8'); |
| #243 | }; |
| #244 | const disableRaw = (): void => { |
| #245 | if (process.stdin.setRawMode) process.stdin.setRawMode(false); |
| #246 | }; |
| #247 | |
| #248 | enableRaw(); |
| #249 | |
| #250 | const onData = (chunk: string): void => { |
| #251 | if (chunk === 'b' || chunk === 'B' || chunk === '\x1b') { |
| #252 | process.stdin.off('data', onData); |
| #253 | disableRaw(); |
| #254 | resolve(); |
| #255 | return; |
| #256 | } |
| #257 | if (chunk === '\x03') { |
| #258 | process.stdin.off('data', onData); |
| #259 | disableRaw(); |
| #260 | process.exit(0); |
| #261 | } |
| #262 | if (chunk === '\x1b[A') { |
| #263 | selected = (selected - 1 + agents.length) % agents.length; |
| #264 | status = `Selected ${agents[selected]?.identifier ?? 'agent'}`; |
| #265 | redraw(); |
| #266 | return; |
| #267 | } |
| #268 | if (chunk === '\x1b[B') { |
| #269 | selected = (selected + 1) % agents.length; |
| #270 | status = `Selected ${agents[selected]?.identifier ?? 'agent'}`; |
| #271 | redraw(); |
| #272 | return; |
| #273 | } |
| #274 | if (chunk === 'm' || chunk === 'M') { |
| #275 | mode = 'preset'; |
| #276 | status = 'Preset mint command staged.'; |
| #277 | redraw(); |
| #278 | return; |
| #279 | } |
| #280 | if (chunk === 'o' || chunk === 'O') { |
| #281 | mode = 'official'; |
| #282 | status = 'Official Metaplex mint command staged.'; |
| #283 | redraw(); |
| #284 | return; |
| #285 | } |
| #286 | if (chunk === 'c' || chunk === 'C') { |
| #287 | mode = 'custom'; |
| #288 | status = 'Custom devnet mint command staged.'; |
| #289 | redraw(); |
| #290 | return; |
| #291 | } |
| #292 | if (chunk === 'g' || chunk === 'G') { |
| #293 | mode = 'registry'; |
| #294 | status = 'Registry discovery commands staged.'; |
| #295 | redraw(); |
| #296 | return; |
| #297 | } |
| #298 | if (chunk === 'r' || chunk === 'R') { |
| #299 | loaded = loadCatalog(); |
| #300 | catalog = loaded.catalog; |
| #301 | source = loaded.source; |
| #302 | agents = (catalog.agents?.length ? catalog.agents : FALLBACK_AGENTS) |
| #303 | .filter((agent) => agent.identifier && agent.title) |
| #304 | .sort((a, b) => a.identifier.localeCompare(b.identifier)); |
| #305 | selected = Math.min(selected, Math.max(0, agents.length - 1)); |
| #306 | status = `Reloaded ${agents.length} agents.`; |
| #307 | redraw(); |
| #308 | } |
| #309 | }; |
| #310 | |
| #311 | process.stdin.on('data', onData); |
| #312 | }); |
| #313 | } |
| #314 |