UNPKG

@agentcommunity/aid-doctor

Version:

CLI tool for Agent Interface Discovery (AID) - validate and check AID records

253 lines (248 loc) 9.11 kB
#!/usr/bin/env node 'use strict'; var commander = require('commander'); var aid = require('@agentcommunity/aid'); var chalk = require('chalk'); var ora = require('ora'); var inquirer = require('inquirer'); var clipboardy = require('clipboardy'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var chalk__default = /*#__PURE__*/_interopDefault(chalk); var ora__default = /*#__PURE__*/_interopDefault(ora); var inquirer__default = /*#__PURE__*/_interopDefault(inquirer); var clipboardy__default = /*#__PURE__*/_interopDefault(clipboardy); function buildTxtRecord(formData) { const parts = ["v=aid1"]; if (formData.uri) parts.push(`uri=${formData.uri}`); const protoKey = parts.join(";").length > 200 ? "p" : "proto"; if (formData.proto) parts.push(`${protoKey}=${formData.proto}`); if (formData.auth) parts.push(`auth=${formData.auth}`); if (formData.desc) parts.push(`desc=${formData.desc}`); return parts.join(";"); } function validateTxtRecord(record) { try { aid.parse(record); return { isValid: true }; } catch (error) { return { isValid: false, error: error instanceof Error ? error.message : "Invalid" }; } } // src/cli.ts var program = new commander.Command(); program.name("aid-doctor").description("CLI tool for Agent Interface Discovery (AID)").version("0.1.0"); function formatDiscoveryResult(result, domain) { const { record, queryName } = result; const lines = [ chalk__default.default.green(`\u2705 AID Record Found for ${domain}`), "", chalk__default.default.bold("Record Details:"), ` Domain: ${chalk__default.default.cyan(domain)}`, ` Query: ${chalk__default.default.gray(queryName)}`, ` Version: ${chalk__default.default.yellow(record.v)}`, ` Protocol: ${chalk__default.default.magenta(record.proto)}`, ` URI: ${chalk__default.default.blue(record.uri)}` ]; if (record.auth) { lines.push(` Auth: ${chalk__default.default.yellow(record.auth)}`); } if (record.desc) { lines.push(` Description: ${chalk__default.default.white(record.desc)}`); } return lines.join("\n"); } function formatError(error, domain) { if (error instanceof aid.AidError) { const codeColor = error.code >= 1003 ? chalk__default.default.red : chalk__default.default.yellow; return [ chalk__default.default.red(`\u274C AID Discovery Failed for ${domain}`), "", ` Error Code: ${codeColor(error.code)} (${error.errorCode})`, ` Message: ${error.message}` ].join("\n"); } return [ chalk__default.default.red(`\u274C Unexpected Error for ${domain}`), "", ` ${error instanceof Error ? error.message : String(error)}` ].join("\n"); } program.command("check <domain>").description("Check a domain for AID records and display a human-readable report").option("-p, --protocol <protocol>", "Try protocol-specific subdomain first").option("-t, --timeout <ms>", "DNS query timeout in milliseconds", "5000").option("--code", "Exit with the specific error code on failure (for scripting)").action( async (domain, options) => { const spinner = ora__default.default(`Checking AID record for ${domain}...`).start(); try { const result = await aid.discover(domain, { ...options.protocol && { protocol: options.protocol }, timeout: Number.parseInt(options.timeout) }); if (result.record.proto !== "local" && process.env.AID_SKIP_SECURITY !== "1") { spinner.text = "Validating redirect policy..."; await aid.enforceRedirectPolicy(result.record.uri, Number.parseInt(options.timeout)); } spinner.stop(); console.log(formatDiscoveryResult(result, domain)); process.exit(0); } catch (error) { spinner.stop(); console.log(formatError(error, domain)); if (options.code && error instanceof aid.AidError) { process.exit(error.code); } else { process.exit(1); } } } ); program.command("json <domain>").description("Check a domain for AID records and output machine-readable JSON").option("-p, --protocol <protocol>", "Try protocol-specific subdomain first").option("-t, --timeout <ms>", "DNS query timeout in milliseconds", "5000").action(async (domain, options) => { try { const result = await aid.discover(domain, { ...options.protocol && { protocol: options.protocol }, timeout: Number.parseInt(options.timeout) }); if (result.record.proto !== "local") { await aid.enforceRedirectPolicy(result.record.uri, Number.parseInt(options.timeout)); } console.log( JSON.stringify( { success: true, domain, queryName: result.queryName, record: result.record, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2 ) ); process.exit(0); } catch (error) { if (error instanceof aid.AidError) { console.log( JSON.stringify( { success: false, domain, error: { code: error.code, errorCode: error.errorCode, message: error.message }, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2 ) ); process.exit(error.code); } else { console.log( JSON.stringify( { success: false, domain, error: { code: 1, errorCode: "UNKNOWN_ERROR", message: error instanceof Error ? error.message : String(error) }, timestamp: (/* @__PURE__ */ new Date()).toISOString() }, null, 2 ) ); process.exit(1); } } }); program.command("generate").description("Run an interactive prompt to generate a new AID record").action(async () => { console.log(chalk__default.default.bold.cyan("AID Record Generator")); console.log(chalk__default.default.gray("This tool will guide you through creating a valid DNS TXT record.\n")); const answers = await inquirer__default.default.prompt([ { type: "input", name: "domain", message: "What is the primary domain for your agent?", validate: (input) => input ? true : "Domain cannot be empty." }, { type: "input", name: "uri", message: "What is the full URI of your agent's endpoint?", validate: (input) => { try { new URL(input); return true; } catch { return "Please enter a valid URI (e.g., https://api.example.com/agent)."; } } }, { type: "list", name: "proto", message: "Select the communication protocol:", choices: Object.keys(aid.PROTOCOL_TOKENS), default: "mcp" }, { type: "list", name: "auth", message: "Select the authentication method (if any):", choices: ["none", ...Object.keys(aid.AUTH_TOKENS)], filter: (val) => val === "none" ? "" : val }, { type: "input", name: "desc", message: "Enter a short description (optional):", validate: (input) => { if (!input) return true; const byteLength = new TextEncoder().encode(input).length; return byteLength <= 60 || `Description is ${byteLength} bytes (max 60).`; } } ]); const formData = answers; const txtRecord = buildTxtRecord(formData); const validation = validateTxtRecord(txtRecord); console.log(chalk__default.default.green("\n--- Generation Complete ---\n")); console.log(chalk__default.default.bold("Host:")); console.log(chalk__default.default.yellow(`_agent.${formData.domain}`)); console.log(""); console.log(chalk__default.default.bold("Type:")); console.log(chalk__default.default.yellow("TXT")); console.log(""); console.log(chalk__default.default.bold("Value:")); console.log(chalk__default.default.yellow(txtRecord)); console.log(""); if (validation.isValid) { try { await clipboardy__default.default.write(txtRecord); console.log( chalk__default.default.bold.green("\u2705 Success! The TXT record value has been copied to your clipboard.") ); } catch { console.log(chalk__default.default.red("Could not copy to clipboard. Please copy the value manually.")); } } else { console.log(chalk__default.default.bold.red(`\u{1F525} Validation Failed: ${validation.error}`)); } }); program.command("version").description("Show version information").action(() => { console.log( [ chalk__default.default.bold("aid-doctor") + " - Agent Interface Discovery CLI", `Version: ${chalk__default.default.green("0.1.0")}`, `Node: ${chalk__default.default.gray(process.version)}`, `Platform: ${chalk__default.default.gray(process.platform)}`, "", "For more information, visit:", chalk__default.default.blue("https://aid.agentcommunity.org") ].join("\n") ); }); program.parse(); //# sourceMappingURL=cli.cjs.map //# sourceMappingURL=cli.cjs.map