UNPKG

mdip-agent-kit

Version:

Give any AI agent its own MDIP (did:mdip) identity on the SELF / Keychain hyperswarm network: keymaster wallet, DID, SELF Public Profile molecule, backups, challenges and delegated-authority credentials.

119 lines (108 loc) • 6.69 kB
#!/usr/bin/env node // mdip-agent CLI. Never prints passphrases or recovery phrases. import fs from 'node:fs'; import { openAgent, loadConfig, SELF_EXPLORER_API } from '../src/index.js'; const HELP = `mdip-agent - give an AI agent its own did:mdip identity (SELF / Keychain MDIP network) Usage: mdip-agent <command> [args] [--home DIR] [--gatekeeper URL] [--name ID] Setup init [--profile-name "Name"] [--no-backup] create wallet + agent DID (idempotent); optionally self-attest SELF Public Profile, back up, verify restore whoami print DID, gatekeeper, explorer visibility profile "<public name>" self-attest SELF Public Profile (published + revealed) backup network (seed bank) + local encrypted file backup verify-backups non-destructive restore test of both backups explorer [did] is the DID visible on explorer.selfid.com? Identity / trust resolve <did> resolve any DID sign <file.json|-> sign JSON with this agent's key (prints signed JSON) verify <file.json|-> verify a signed JSON object challenge [--schema DID]... [--issuer DID]... [--callback URL] create a challenge DID for another party to answer respond <challengeDid> [--valid-days N] answer a challenge -> response DID (valid N days, default 7, capped at the challenge's validUntil) verify-response <responseDid> verify a response to your challenge accept <credentialDid> accept a credential issued to this agent credentials list held credentials credential <did> show one held credential (decrypted) Env: MDIP_HOME (default ~/.mdip-agent), MDIP_GATEKEEPER_URL (default http://localhost:4224), MDIP_AGENT_NAME (default "agent"), MDIP_REGISTRY (hyperswarm), MDIP_DID_PREFIX (did:mdip)`; function parse(argv) { const args = [], flags = {}; for (let i = 0; i < argv.length; i++) { const a = argv[i]; if (a.startsWith('--')) { const [k, v] = a.slice(2).split('=', 2); const val = v !== undefined ? v : (argv[i + 1] && !argv[i + 1].startsWith('--') ? argv[++i] : true); if (flags[k] === undefined) flags[k] = val; else flags[k] = [].concat(flags[k], val); } else args.push(a); } return { args, flags }; } const list = v => v === undefined ? [] : [].concat(v); const out = v => console.log(typeof v === 'string' ? v : JSON.stringify(v, null, 2)); const readJson = f => JSON.parse(f === '-' ? fs.readFileSync(0, 'utf8') : fs.readFileSync(f, 'utf8')); async function main() { const { args, flags } = parse(process.argv.slice(2)); const cmd = args.shift(); if (!cmd || cmd === 'help' || flags.help) return out(HELP); const opts = { home: flags.home, gatekeeperUrl: flags.gatekeeper, name: flags.name }; if (cmd === 'init') { const agent = await openAgent({ ...opts, create: true }); const cfg = agent.config; out(`agent DID: ${agent.did}`); out(`wallet: ${cfg.paths.wallet} (secrets in ${cfg.home}/secrets, mode 600)`); if (flags['profile-name']) { const p = await agent.selfAttestPublicProfile(String(flags['profile-name'])); out(`public profile molecule: ${p.credentialDid} (publicName revealed on manifest)`); } if (!flags['no-backup']) { const b = await agent.backup(); out(`network wallet backup DID: ${b.walletBackupDid}`); out(`local encrypted backup: ${b.file}`); const v = await agent.verifyBackups(); out(v.results); if (!v.pass) process.exitCode = 2; } out(`check publication: ${SELF_EXPLORER_API}${agent.did}`); return; } const agent = await openAgent(opts); switch (cmd) { case 'whoami': { const s = agent.state; return out({ name: agent.config.name, did: agent.did, gatekeeper: agent.config.gatekeeperUrl, registry: agent.config.registry, publicProfile: s.publicProfile, walletBackupDid: s.walletBackupDid, explorer: await agent.explorerStatus().catch(e => ({ error: e.message })) }); } case 'profile': { if (!args[0]) throw new Error('usage: profile "<public name>"'); return out(await agent.selfAttestPublicProfile(args[0])); } case 'backup': return out(await agent.backup()); case 'verify-backups': { const v = await agent.verifyBackups(); out(v); if (!v.pass) process.exitCode = 2; return; } case 'explorer': return out(await agent.explorerStatus(args[0] || agent.did)); case 'resolve': return out(await agent.resolve(args[0])); case 'sign': return out(await agent.sign(readJson(args[0] || '-'))); case 'verify': { const ok = await agent.verify(readJson(args[0] || '-')); out({ valid: ok }); if (!ok) process.exitCode = 1; return; } case 'challenge': { const issuers = list(flags.issuer); const credentials = list(flags.schema).map(schema => issuers.length ? { schema, issuers } : { schema }); const challenge = {}; if (credentials.length) challenge.credentials = credentials; if (flags.callback) challenge.callback = String(flags.callback); return out(await agent.createChallenge(challenge)); } case 'respond': { if (!args[0]) throw new Error('usage: respond <challengeDid> [--valid-days N]'); const o = flags['valid-days'] !== undefined ? { validDays: Number(flags['valid-days']) } : {}; return out(await agent.respondToChallenge(args[0], o)); } case 'verify-response': { const r = await agent.verifyResponse(args[0]); out(r); if (!r.match) process.exitCode = 1; return; } case 'accept': { const ok = await agent.acceptCredential(args[0]); out({ accepted: ok, credential: args[0] }); return; } case 'credentials': return out(await agent.listCredentials()); case 'credential': return out(await agent.getCredential(args[0])); default: throw new Error(`unknown command: ${cmd} (try --help)`); } } main().then(() => process.exit(process.exitCode || 0)).catch(e => { console.error('error:', e?.message || e?.error || e); process.exit(1); });