UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

206 lines 7.85 kB
/** * AgentCard CLI handler — `aiwg agentcard <verb> ...`. * * Verbs: * verify Fetch an agentic-sandbox AgentCard from the well-known route, * /v1/extendedAgentCard, or legacy /v1/card, then verify its * Ed25519 JWS signature against a supplied JWKS. * * @implements @src/a2a/agent-card.ts * @issue #1253 */ import { fetchAgentCard } from '../../a2a/agent-card.js'; import { loadJwkSet } from '../../a2a/jws.js'; import { AiwgError, EXIT_CODES, handlerResultFromError } from '../errors.js'; function parseVerifyArgs(args) { const get = (name) => { const idx = args.indexOf(name); if (idx >= 0 && idx + 1 < args.length) return args[idx + 1]; return undefined; }; const host = get('--host'); const instance = get('--instance'); if (!host) { throw new AiwgError({ code: 'ERR_AGENTCARD_MISSING_HOST', message: 'Missing --host', exitCode: EXIT_CODES.USAGE, hint: 'Example: aiwg agentcard verify --host https://exec.test --instance inst-1 --jwks /path/to/jwks.json', }); } if (!instance) { throw new AiwgError({ code: 'ERR_AGENTCARD_MISSING_INSTANCE', message: 'Missing --instance', exitCode: EXIT_CODES.USAGE, hint: 'Example: aiwg agentcard verify --host https://exec.test --instance inst-1 --jwks /path/to/jwks.json', }); } const jwks = get('--jwks'); const bearer = get('--bearer'); const skipVerify = args.includes('--skip-verify'); const json = args.includes('--json'); if (!jwks && !skipVerify) { throw new AiwgError({ code: 'ERR_AGENTCARD_MISSING_JWKS', message: '--jwks is required unless --skip-verify is set', exitCode: EXIT_CODES.USAGE, hint: 'Provide a JWKS file path or URL; or pass --skip-verify to bypass signature verification.', }); } const result = { host, instance, skipVerify, json }; if (jwks !== undefined) result.jwks = jwks; if (bearer !== undefined) result.bearer = bearer; return result; } async function loadJwksFromSource(source) { if (/^https?:\/\//i.test(source)) { const resp = await fetch(source); if (resp.status !== 200) { throw new AiwgError({ code: 'ERR_AGENTCARD_JWKS_FETCH', message: `JWKS fetch failed: ${source} returned ${resp.status}`, exitCode: EXIT_CODES.GENERAL, }); } return loadJwkSet(await resp.text()); } const { readFile } = await import('node:fs/promises'); return loadJwkSet(await readFile(source, 'utf8')); } async function handleVerify(args) { const parsed = parseVerifyArgs(args); const opts = { skipVerify: parsed.skipVerify, }; if (parsed.bearer !== undefined) opts.bearer = parsed.bearer; if (parsed.jwks !== undefined && !parsed.skipVerify) { opts.jwks = await loadJwksFromSource(parsed.jwks); } let verified; try { verified = await fetchAgentCard(parsed.host, parsed.instance, opts); } catch (err) { const message = err.message; if (parsed.json) { console.log(JSON.stringify({ ok: false, host: parsed.host, instance: parsed.instance, error: message, }, null, 2)); } else { console.error(`AgentCard verification FAILED for ${parsed.host}/${parsed.instance}`); console.error(` ${message}`); } return { exitCode: EXIT_CODES.GENERAL }; } if (parsed.json) { console.log(JSON.stringify({ ok: true, host: parsed.host, instance: parsed.instance, verifiedAt: verified.verifiedAt, kid: verified.kid, skipVerify: parsed.skipVerify, card: { name: verified.card.name, protocolVersion: verified.card.protocolVersion, url: verified.card.url, version: verified.card.version, preferredTransport: verified.card.preferredTransport, capabilities: verified.card.capabilities, skills: verified.card.skills, }, }, null, 2)); } else { const banner = parsed.skipVerify ? '(verification SKIPPED)' : 'VERIFIED'; console.log(`AgentCard ${banner} for ${parsed.host}/${parsed.instance}`); console.log(` name: ${verified.card.name}`); console.log(` protocolVersion: ${verified.card.protocolVersion}`); console.log(` url: ${verified.card.url}`); console.log(` version: ${verified.card.version}`); if (verified.card.preferredTransport) { console.log(` preferredTransport: ${verified.card.preferredTransport}`); } if (verified.kid) { console.log(` signing kid: ${verified.kid}`); } console.log(` verifiedAt: ${verified.verifiedAt}`); const exts = verified.card.capabilities?.extensions ?? []; if (exts.length > 0) { console.log(` extensions:`); for (const e of exts) { const tag = e.required ? '[required]' : '[optional]'; console.log(` ${tag} ${e.uri}`); } } const skills = verified.card.skills ?? []; if (skills.length > 0) { console.log(` skills (${skills.length}):`); for (const s of skills.slice(0, 5)) { console.log(` - ${s.id}${s.name ? ' (' + s.name + ')' : ''}`); } if (skills.length > 5) console.log(` ... and ${skills.length - 5} more`); } } return { exitCode: 0 }; } function printUsage() { console.log('Usage: aiwg agentcard <verb> [options]'); console.log(''); console.log('Verbs:'); console.log(' verify Fetch and verify an AgentCard JWS signature'); console.log(''); console.log('verify options:'); console.log(' --host <url> Executor host (e.g. https://exec.test)'); console.log(' --instance <id> Instance id (UUIDv7)'); console.log(' --jwks <path|url> JWKS file path or URL (required unless --skip-verify)'); console.log(' --bearer <token> Optional bearer token for the card endpoint'); console.log(' --skip-verify Skip JWS verification (NOT recommended)'); console.log(' --json Emit JSON output'); } /** * agentcard command handler — dispatches on the first positional arg. */ export const agentcardHandler = { id: 'agentcard', name: 'AgentCard', description: 'Fetch and verify an agentic-sandbox AgentCard', category: 'toolsmith', aliases: [], async execute(ctx) { try { const args = ctx.args; const verb = args[0]; if (!verb || verb === '--help' || verb === '-h') { printUsage(); return { exitCode: verb ? 0 : EXIT_CODES.USAGE }; } const rest = args.slice(1); switch (verb) { case 'verify': return await handleVerify(rest); default: throw new AiwgError({ code: 'ERR_AGENTCARD_UNKNOWN_VERB', message: `Unknown verb: ${verb}`, exitCode: EXIT_CODES.USAGE, hint: "Run 'aiwg agentcard --help' for usage.", }); } } catch (err) { return handlerResultFromError(err); } }, }; //# sourceMappingURL=agentcard.js.map