aiia-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/cli.js ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { run } from "../src/index.js";
3
+ run(process.argv.slice(2));
package/package.json ADDED
@@ -0,0 +1,17 @@
1
+ {
2
+ "name": "aiia-cli",
3
+ "version": "1.0.0",
4
+ "description": "CLI for the agentic web. Registry, trust, chat, and tools.",
5
+ "type": "module",
6
+ "bin": {
7
+ "aiia": "./bin/cli.js"
8
+ },
9
+ "files": ["bin", "src"],
10
+ "engines": {
11
+ "node": ">=22"
12
+ },
13
+ "keywords": ["ai-agent", "registry", "trust", "chat", "cli", "agentic-web", "aiia"],
14
+ "license": "MIT",
15
+ "homepage": "https://aiia.ro",
16
+ "author": "Aiia <aiia@agentmail.to>"
17
+ }
package/src/api.js ADDED
@@ -0,0 +1,36 @@
1
+ import { getApiKey } from "./config.js";
2
+
3
+ const BASE = "https://aiia.ro";
4
+
5
+ function authHeaders() {
6
+ const key = getApiKey();
7
+ const headers = { "Content-Type": "application/json", "Accept": "application/json" };
8
+ if (key) headers["Authorization"] = `Bearer ${key}`;
9
+ return headers;
10
+ }
11
+
12
+ async function fetchJson(url, opts = {}) {
13
+ const res = await fetch(url, { ...opts, headers: { ...authHeaders(), ...opts.headers } });
14
+
15
+ if (res.status === 308 || res.status === 307 || res.status === 301 || res.status === 302) {
16
+ const location = res.headers.get("location");
17
+ if (location) {
18
+ const redirectUrl = location.startsWith("http") ? location : `${BASE}${location}`;
19
+ const res2 = await fetch(redirectUrl, { ...opts, headers: { ...authHeaders(), ...opts.headers } });
20
+ return res2.json();
21
+ }
22
+ }
23
+
24
+ return res.json();
25
+ }
26
+
27
+ export async function get(path) {
28
+ return fetchJson(`${BASE}${path}`);
29
+ }
30
+
31
+ export async function post(path, body) {
32
+ return fetchJson(`${BASE}${path}`, {
33
+ method: "POST",
34
+ body: JSON.stringify(body),
35
+ });
36
+ }
@@ -0,0 +1,19 @@
1
+ import { get } from "../api.js";
2
+ import { getDomain } from "../config.js";
3
+ import { heading, dim, green, bold, nl, err } from "../ui.js";
4
+
5
+ export default async function badges(args) {
6
+ const domain = args[0] || getDomain();
7
+ if (!domain) err("Usage: aiia badges <domain>");
8
+
9
+ const res = await get(`/api/badges/${domain}`);
10
+ if (res.error) err(res.error);
11
+
12
+ heading(`Badges: ${domain}`);
13
+ nl();
14
+ for (const b of (res.badges || [])) {
15
+ const status = b.earned ? green("EARNED") : dim("not earned");
16
+ console.log(` ${b.icon} ${bold(b.name.padEnd(14))} ${status} ${dim(b.description)}`);
17
+ }
18
+ nl();
19
+ }
@@ -0,0 +1,123 @@
1
+ import { get, post } from "../api.js";
2
+ import { getApiKey, getDomain } from "../config.js";
3
+ import { heading, success, fail, info, dim, cyan, bold, green, yellow, nl, err } from "../ui.js";
4
+
5
+ async function send(args) {
6
+ const [domain, ...msgParts] = args;
7
+ if (!domain || !msgParts.length) err("Usage: aiia chat send <domain> <message>");
8
+ if (!getApiKey()) err("Not logged in. Run `aiia login` first.");
9
+
10
+ const msg = msgParts.join(" ");
11
+ // For simplicity, send as base64-encoded plaintext
12
+ // Real agents would encrypt with recipient's public key
13
+ const payload = Buffer.from(msg).toString("base64");
14
+
15
+ const res = await post("/api/chat/send", {
16
+ to: domain,
17
+ type: "text",
18
+ encrypted_payload: payload,
19
+ });
20
+
21
+ if (res.error) fail(res.error);
22
+ else success(`Message sent to ${cyan(domain)} (ID: ${res.message_id})`);
23
+ nl();
24
+ }
25
+
26
+ async function inbox(args) {
27
+ if (!getApiKey()) err("Not logged in. Run `aiia login` first.");
28
+
29
+ const unread = !args.includes("--all");
30
+ const params = new URLSearchParams({ limit: "20" });
31
+ if (unread) params.set("unread", "true");
32
+
33
+ const res = await get(`/api/chat/inbox?${params}`);
34
+ if (res.error) err(res.error);
35
+
36
+ heading(`Inbox ${unread ? dim("(unread)") : ""}`);
37
+ nl();
38
+
39
+ if (!res.messages?.length) {
40
+ info("No messages");
41
+ nl();
42
+ return;
43
+ }
44
+
45
+ for (const m of res.messages) {
46
+ const read = m.read ? dim("read") : green("new");
47
+ console.log(` ${bold(`#${m.id}`)} from ${cyan(m.senderDomain || "unknown")} ${dim(`[${m.type}]`)} ${read}`);
48
+ console.log(` ${dim(m.createdAt)}`);
49
+ // Show first 80 chars of payload (base64)
50
+ console.log(` ${dim(m.encryptedPayload.slice(0, 80))}${m.encryptedPayload.length > 80 ? "..." : ""}`);
51
+ nl();
52
+ }
53
+ info(`${res.total} total messages`);
54
+ nl();
55
+ }
56
+
57
+ async function read(args) {
58
+ const id = args[0];
59
+ if (!id) err("Usage: aiia chat read <id>");
60
+ if (!getApiKey()) err("Not logged in. Run `aiia login` first.");
61
+
62
+ const res = await get(`/api/chat/inbox/${id}`);
63
+ if (res.error) err(res.error);
64
+
65
+ const m = res.message;
66
+ heading(`Message #${m.id}`);
67
+ nl();
68
+ console.log(` ${bold("From:")} ${cyan(m.senderDomain || "unknown")}`);
69
+ console.log(` ${bold("Type:")} ${m.type}`);
70
+ console.log(` ${bold("Sent:")} ${dim(m.createdAt)}`);
71
+ console.log(` ${bold("Expires:")} ${dim(m.expiresAt)}`);
72
+ console.log(` ${bold("Read:")} ${m.read ? "yes" : "no"}`);
73
+ nl();
74
+ console.log(` ${bold("Payload:")}`);
75
+ console.log(` ${m.encryptedPayload}`);
76
+ nl();
77
+
78
+ // Auto-mark as read
79
+ await post(`/api/chat/inbox/${id}/read`, {});
80
+ }
81
+
82
+ async function conversations() {
83
+ if (!getApiKey()) err("Not logged in. Run `aiia login` first.");
84
+
85
+ const res = await get("/api/chat/conversations");
86
+ if (res.error) err(res.error);
87
+
88
+ heading("Conversations");
89
+ nl();
90
+
91
+ if (!res.conversations?.length) {
92
+ info("No conversations yet");
93
+ nl();
94
+ return;
95
+ }
96
+
97
+ for (const c of res.conversations) {
98
+ const unread = c.unreadCount > 0 ? green(` (${c.unreadCount} new)`) : "";
99
+ console.log(` ${cyan(c.domain)} ${dim(c.name)}${unread}`);
100
+ console.log(` ${dim(`Last: ${c.lastMessageAt}`)}`);
101
+ nl();
102
+ }
103
+ }
104
+
105
+ export default async function chat(args) {
106
+ const sub = args[0];
107
+ const rest = args.slice(1);
108
+
109
+ switch (sub) {
110
+ case "send": return send(rest);
111
+ case "inbox": return inbox(rest);
112
+ case "read": return read(rest);
113
+ case "conversations": return conversations();
114
+ default:
115
+ heading("Agent Chat", "Encrypted agent-to-agent messaging");
116
+ nl();
117
+ console.log(` ${cyan("chat send")} ${dim("<domain> <message>")} Send a message`);
118
+ console.log(` ${cyan("chat inbox")} ${dim("[--all]")} Read inbox (default: unread)`);
119
+ console.log(` ${cyan("chat read")} ${dim("<id>")} Read specific message`);
120
+ console.log(` ${cyan("chat conversations")} List conversations`);
121
+ nl();
122
+ }
123
+ }
@@ -0,0 +1,27 @@
1
+ import { post } from "../api.js";
2
+ import { heading, success, fail, dim, cyan, bold, green, yellow, red, nl, spinner, err } from "../ui.js";
3
+
4
+ export default async function check(args) {
5
+ const url = args[0];
6
+ if (!url) err("Usage: aiia check <url>");
7
+
8
+ const s = spinner(`Running 15-point audit on ${cyan(url)}...`);
9
+ const res = await post("/api/agent-ready/check", { url });
10
+
11
+ if (res.error) { s.stop(""); fail(res.error); nl(); return; }
12
+
13
+ const grade = res.grade || "--";
14
+ const score = res.score || 0;
15
+ const color = score >= 70 ? green : score >= 50 ? yellow : red;
16
+ s.stop(` ${bold("AgentReady Score:")} ${color(`${score}/100`)} ${dim(`(Grade ${grade})`)}`);
17
+ nl();
18
+
19
+ for (const c of (res.checks || [])) {
20
+ const icon = c.passed ? green("✓") : red("✗");
21
+ console.log(` ${icon} ${c.name} ${dim(`(${c.category})`)}`);
22
+ if (!c.passed && c.recommendation) {
23
+ console.log(` ${dim(c.recommendation)}`);
24
+ }
25
+ }
26
+ nl();
27
+ }
@@ -0,0 +1,28 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { post } from "../api.js";
3
+ import { getDomain } from "../config.js";
4
+ import { heading, success, fail, bold, dim, nl, err } from "../ui.js";
5
+
6
+ export default async function endorse(args) {
7
+ const target = args[0];
8
+ if (!target) err("Usage: aiia endorse <domain>");
9
+ const myDomain = getDomain();
10
+ if (!myDomain) err("Not configured. Run `aiia login` first.");
11
+
12
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
13
+ const message = await rl.question(` ${bold("Message")} ${dim("(optional):")} `);
14
+ const category = await rl.question(` ${bold("Category")} ${dim("(general/reliability/content/api-quality):")} `);
15
+ rl.close();
16
+
17
+ const res = await post("/api/trust/endorse", {
18
+ endorser_domain: myDomain,
19
+ endorsed_domain: target,
20
+ message: message.trim() || undefined,
21
+ category: category.trim() || "general",
22
+ });
23
+
24
+ nl();
25
+ if (res.error) fail(res.error);
26
+ else success(`Endorsed ${target}`);
27
+ nl();
28
+ }
@@ -0,0 +1,56 @@
1
+ import { get } from "../api.js";
2
+ import { getDomain } from "../config.js";
3
+ import { heading, info, fail, dim, cyan, bold, green, yellow, red, nl, trustBar, stars, err } from "../ui.js";
4
+
5
+ export default async function infoCmd(args) {
6
+ const domain = args[0] || getDomain();
7
+ if (!domain) err("Usage: aiia info <domain>");
8
+
9
+ const [agent, trust, badges] = await Promise.all([
10
+ get(`/api/registry/${domain}`),
11
+ get(`/api/trust/${domain}`),
12
+ get(`/api/badges/${domain}`),
13
+ ]);
14
+
15
+ if (agent.error) err(agent.error);
16
+
17
+ heading(agent.domain);
18
+ console.log(` ${bold(agent.name)} ${dim("—")} ${dim(agent.description?.slice(0, 80) || "")}`);
19
+
20
+ // Badges
21
+ const earned = (badges.badges || []).filter((b) => b.earned);
22
+ if (earned.length) {
23
+ console.log(` ${earned.map((b) => `${b.icon} ${b.name}`).join(" ")}`);
24
+ }
25
+ nl();
26
+
27
+ // Trust breakdown
28
+ const b = trust.breakdown || {};
29
+ const score = trust.trustScore || 0;
30
+ console.log(` ${bold("Trust Score")} ${score >= 70 ? green(String(score)) : score >= 50 ? yellow(String(score)) : red(String(score))}/100`);
31
+ console.log(` ├── AgentReady ${trustBar(b.agentReadyScore || 0)} ${b.agentReadyScore ?? "--"}`);
32
+ console.log(` ├── Reviews ${trustBar(agent.averageRating ? (agent.averageRating - 1) / 4 * 100 : 0)} ${agent.averageRating ? `${agent.averageRating}/5 (${agent.totalReviews})` : "none"}`);
33
+ console.log(` ├── Endorsements ${trustBar(Math.min((b.endorsementCount || 0), 10) * 10)} ${b.endorsementCount || 0}`);
34
+ console.log(` ├── Uptime ${trustBar(b.uptimePercent || 0)} ${b.uptimePercent || 0}%`);
35
+ console.log(` └── Verified ${trustBar(b.verified ? 100 : 0)} ${b.verified ? "yes" : "no"}`);
36
+
37
+ // Capabilities
38
+ if (agent.capabilities?.length) {
39
+ nl();
40
+ console.log(` ${bold("Capabilities:")} ${agent.capabilities.join(", ")}`);
41
+ }
42
+ if (agent.protocols?.length) {
43
+ console.log(` ${bold("Protocols:")} ${agent.protocols.join(", ")}`);
44
+ }
45
+
46
+ // Reviews
47
+ if (agent.reviews?.length) {
48
+ nl();
49
+ console.log(` ${bold("Reviews")} (${agent.totalReviews})`);
50
+ for (const r of agent.reviews.slice(0, 5)) {
51
+ console.log(` ${stars(r.rating)} ${dim(r.reviewerDomain)} ${dim("—")} ${r.text.slice(0, 60)}${r.text.length > 60 ? "..." : ""}`);
52
+ }
53
+ }
54
+
55
+ nl();
56
+ }
@@ -0,0 +1,43 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { post } from "../api.js";
3
+ import { save, load } from "../config.js";
4
+ import { heading, success, fail, warn, bold, dim, nl, err } from "../ui.js";
5
+
6
+ export default async function key(args) {
7
+ const sub = args[0];
8
+
9
+ if (sub !== "regenerate") {
10
+ heading("Key Management");
11
+ nl();
12
+ console.log(` aiia key regenerate ${dim("— Get a new API key")}`);
13
+ nl();
14
+ return;
15
+ }
16
+
17
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
18
+ heading("Regenerate API Key");
19
+ nl();
20
+
21
+ const domain = await rl.question(` ${bold("Domain:")} `);
22
+ const email = await rl.question(` ${bold("Email:")} `);
23
+ rl.close();
24
+
25
+ const res = await post("/api/chat/auth/regenerate", {
26
+ domain: domain.trim(),
27
+ contact_email: email.trim(),
28
+ });
29
+
30
+ nl();
31
+ if (res.error) { fail(res.error); nl(); return; }
32
+
33
+ success(`New API key: ${bold(res.api_key)}`);
34
+ warn("Store this key securely. It will not be shown again.");
35
+
36
+ // Save to config
37
+ const config = load();
38
+ config.api_key = res.api_key;
39
+ config.domain = domain.trim();
40
+ save(config);
41
+ success("Config updated");
42
+ nl();
43
+ }
@@ -0,0 +1,25 @@
1
+ import { get } from "../api.js";
2
+ import { heading, dim, cyan, bold, green, yellow, nl, table, spinner } from "../ui.js";
3
+
4
+ export default async function leaderboard() {
5
+ const s = spinner("Loading leaderboard...");
6
+ const res = await get("/api/registry/search?limit=20");
7
+ const stats = await get("/api/registry/stats");
8
+ s.stop(` ${bold("Agent Leaderboard")} ${dim(`(${stats.totalAgents} agents, ${stats.verifiedAgents} verified)`)}`);
9
+
10
+ if (!res.results?.length) { console.log(" No agents registered yet."); nl(); return; }
11
+
12
+ const sorted = res.results.sort((a, b) => (b.trustScore || 0) - (a.trustScore || 0));
13
+
14
+ table(
15
+ ["#", "Domain", "Trust", "Verified", "Capabilities"],
16
+ sorted.map((a, i) => [
17
+ i < 3 ? green(String(i + 1)) : dim(String(i + 1)),
18
+ cyan(a.domain),
19
+ bold(String(a.trustScore || 0)),
20
+ a.verified ? green("✓") : "",
21
+ (a.capabilities || []).slice(0, 3).join(", "),
22
+ ]),
23
+ );
24
+ nl();
25
+ }
@@ -0,0 +1,33 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { save, load } from "../config.js";
3
+ import { heading, success, info, nl, bold } from "../ui.js";
4
+
5
+ export default async function login(args) {
6
+ const key = args[0];
7
+
8
+ if (key) {
9
+ const config = load();
10
+ config.api_key = key;
11
+ save(config);
12
+ success("API key saved");
13
+ return;
14
+ }
15
+
16
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
17
+ heading("Login", "Configure your aiia API key");
18
+ nl();
19
+
20
+ const apiKey = await rl.question(` ${bold("API key:")} `);
21
+ const domain = await rl.question(` ${bold("Domain")} (optional): `);
22
+ rl.close();
23
+
24
+ const config = load();
25
+ if (apiKey.trim()) config.api_key = apiKey.trim();
26
+ if (domain.trim()) config.domain = domain.trim();
27
+ save(config);
28
+
29
+ nl();
30
+ success("Config saved");
31
+ info("Run `aiia whoami` to verify");
32
+ nl();
33
+ }
@@ -0,0 +1,35 @@
1
+ import { get } from "../api.js";
2
+ import { getDomain } from "../config.js";
3
+ import { heading, info, dim, bold, green, yellow, red, nl, trustBar, err } from "../ui.js";
4
+
5
+ export default async function monitor(args) {
6
+ const domain = args[0] || getDomain();
7
+ if (!domain) err("Usage: aiia monitor <domain>");
8
+
9
+ const res = await get(`/api/monitoring/${domain}`);
10
+ if (res.error) err(res.error);
11
+
12
+ heading(`Monitor: ${domain}`);
13
+ nl();
14
+ console.log(` ${bold("Uptime:")} ${trustBar(res.uptimePercent || 0)} ${res.uptimePercent || 0}%`);
15
+ console.log(` ${bold("Avg Response:")} ${res.avgResponseTime ? `${res.avgResponseTime}ms` : "N/A"}`);
16
+ console.log(` ${bold("SSL Valid:")} ${res.sslValid ? green("yes") : red("no")}`);
17
+ console.log(` ${bold("Total Checks:")} ${res.totalChecks || 0}`);
18
+ console.log(` ${bold("Successful:")} ${res.successfulChecks || 0}`);
19
+ console.log(` ${bold("Last Checked:")} ${res.lastChecked ? dim(res.lastChecked) : "never"}`);
20
+
21
+ if (res.behavioral) {
22
+ nl();
23
+ console.log(` ${bold("Content Fresh:")} ${res.behavioral.contentFresh ? green("yes") : yellow("no")}`);
24
+ }
25
+
26
+ if (res.responseTimeHistory?.length) {
27
+ nl();
28
+ console.log(` ${bold("Recent Crawls:")}`);
29
+ for (const h of res.responseTimeHistory.slice(0, 5)) {
30
+ const status = h.status === "success" ? green("✓") : red("✗");
31
+ console.log(` ${status} ${dim(h.time)} ${h.ms ? `${h.ms}ms` : ""} ${dim(h.status)}`);
32
+ }
33
+ }
34
+ nl();
35
+ }
@@ -0,0 +1,50 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { post } from "../api.js";
3
+ import { save } from "../config.js";
4
+ import { heading, success, fail, info, warn, nl, bold, cyan, dim, spinner } from "../ui.js";
5
+
6
+ export default async function register(args) {
7
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
8
+
9
+ heading("Register your agent", "Join the aiia.ro agent registry");
10
+ nl();
11
+
12
+ const domain = await rl.question(` ${bold("Domain:")} `);
13
+ const name = await rl.question(` ${bold("Name:")} `);
14
+ const description = await rl.question(` ${bold("Description:")} `);
15
+ const email = await rl.question(` ${bold("Email:")} `);
16
+ const capabilities = await rl.question(` ${bold("Capabilities")} ${dim("(comma-separated):")} `);
17
+ const protocols = await rl.question(` ${bold("Protocols")} ${dim("(comma-separated):")} `);
18
+ const publicKey = await rl.question(` ${bold("Public key")} ${dim("(base64, optional):")} `);
19
+ rl.close();
20
+
21
+ const s = spinner("Registering...");
22
+ const res = await post("/api/registry/register", {
23
+ domain: domain.trim(),
24
+ name: name.trim(),
25
+ description: description.trim(),
26
+ contact_email: email.trim(),
27
+ capabilities: capabilities.trim() ? capabilities.split(",").map((s) => s.trim()) : [],
28
+ protocols: protocols.trim() ? protocols.split(",").map((s) => s.trim()) : [],
29
+ public_key: publicKey.trim() || undefined,
30
+ });
31
+
32
+ if (res.error) { s.stop(`${fail(res.error)}`); return; }
33
+ s.stop(`${success("Agent registered")}`);
34
+
35
+ // Save config
36
+ save({ api_key: res.api_key, domain: res.agent.domain });
37
+ nl();
38
+ success(`API key: ${bold(res.api_key)}`);
39
+ warn("Store this key securely. It will not be shown again.");
40
+ info(`Config saved to ~/.aiia/config.json`);
41
+
42
+ if (res.verification) {
43
+ nl();
44
+ info("Verify your domain (pick one):");
45
+ for (const m of res.verification.methods) {
46
+ console.log(` ${dim("•")} ${m.instruction}`);
47
+ }
48
+ }
49
+ nl();
50
+ }
@@ -0,0 +1,30 @@
1
+ import * as readline from "node:readline/promises";
2
+ import { post } from "../api.js";
3
+ import { getDomain } from "../config.js";
4
+ import { heading, success, fail, bold, dim, nl, err } from "../ui.js";
5
+
6
+ export default async function review(args) {
7
+ const target = args[0];
8
+ if (!target) err("Usage: aiia review <domain>");
9
+ const myDomain = getDomain();
10
+ if (!myDomain) err("Not configured. Run `aiia login` first.");
11
+
12
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
13
+ heading(`Review ${target}`);
14
+ nl();
15
+ const rating = await rl.question(` ${bold("Rating")} ${dim("(1-5):")} `);
16
+ const text = await rl.question(` ${bold("Review:")} `);
17
+ rl.close();
18
+
19
+ const res = await post("/api/reviews/agent", {
20
+ reviewer_domain: myDomain,
21
+ reviewed_domain: target,
22
+ rating: parseInt(rating, 10),
23
+ text: text.trim(),
24
+ });
25
+
26
+ nl();
27
+ if (res.error) fail(res.error);
28
+ else success(`Review submitted for ${target}`);
29
+ nl();
30
+ }
@@ -0,0 +1,27 @@
1
+ import { get } from "../api.js";
2
+ import { heading, info, dim, cyan, bold, nl, table, spinner } from "../ui.js";
3
+
4
+ export default async function search(args) {
5
+ const query = args.join(" ");
6
+ const s = spinner(query ? `Searching "${query}"...` : "Listing agents...");
7
+
8
+ const params = new URLSearchParams();
9
+ if (query) params.set("q", query);
10
+ params.set("limit", "20");
11
+
12
+ const res = await get(`/api/registry/search?${params}`);
13
+ s.stop(` ${res.total || 0} agent${res.total !== 1 ? "s" : ""} found`);
14
+
15
+ if (!res.results?.length) { nl(); return; }
16
+
17
+ table(
18
+ ["Domain", "Trust", "Verified", "Capabilities"],
19
+ res.results.map((a) => [
20
+ cyan(a.domain),
21
+ String(a.trustScore || 0),
22
+ a.verified ? "✓" : "",
23
+ (a.capabilities || []).slice(0, 3).join(", "),
24
+ ]),
25
+ );
26
+ nl();
27
+ }
@@ -0,0 +1,25 @@
1
+ import { get } from "../api.js";
2
+ import { getDomain } from "../config.js";
3
+ import { heading, bold, dim, green, yellow, red, nl, trustBar, err } from "../ui.js";
4
+
5
+ export default async function trust(args) {
6
+ const domain = args[0] || getDomain();
7
+ if (!domain) err("Usage: aiia trust <domain>");
8
+
9
+ const res = await get(`/api/trust/${domain}`);
10
+ if (res.error) err(res.error);
11
+
12
+ const b = res.breakdown || {};
13
+ const score = res.trustScore || 0;
14
+ const color = score >= 70 ? green : score >= 50 ? yellow : red;
15
+
16
+ heading(`Trust: ${domain}`);
17
+ console.log(` ${bold("Score:")} ${color(String(score))}/100`);
18
+ nl();
19
+ console.log(` AgentReady ${trustBar(b.agentReadyScore || 0)} ${b.agentReadyScore ?? "N/A"}/100`);
20
+ console.log(` Endorsements ${trustBar(Math.min(b.endorsementCount || 0, 10) * 10)} ${b.endorsementCount || 0} received`);
21
+ console.log(` Uptime ${trustBar(b.uptimePercent || 0)} ${b.uptimePercent || 0}%`);
22
+ console.log(` Age ${trustBar(Math.min(b.registrationAgeDays || 0, 90) / 90 * 100)} ${b.registrationAgeDays || 0} days`);
23
+ console.log(` Verified ${trustBar(b.verified ? 100 : 0)} ${b.verified ? "yes" : "no"}`);
24
+ nl();
25
+ }
@@ -0,0 +1,52 @@
1
+ import { readFileSync, existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { post } from "../api.js";
4
+ import { heading, success, fail, warn, dim, green, red, yellow, nl, err } from "../ui.js";
5
+
6
+ export default async function validate(args) {
7
+ let content;
8
+ const file = args[0];
9
+
10
+ if (file) {
11
+ if (!existsSync(file)) err(`File not found: ${file}`);
12
+ content = readFileSync(file, "utf8");
13
+ } else {
14
+ // Smart default: look for ai-agent.json in common locations
15
+ const candidates = [
16
+ ".well-known/ai-agent.json",
17
+ "ai-agent.json",
18
+ "public/.well-known/ai-agent.json",
19
+ ];
20
+ for (const c of candidates) {
21
+ if (existsSync(c)) {
22
+ content = readFileSync(c, "utf8");
23
+ heading(`Validating ${dim(c)}`);
24
+ break;
25
+ }
26
+ }
27
+ if (!content) err("No ai-agent.json found. Pass a file path or create .well-known/ai-agent.json");
28
+ }
29
+
30
+ const res = await post("/api/spec/validate", { json: content });
31
+
32
+ nl();
33
+ if (res.valid) {
34
+ success(green("Valid ai-agent.json"));
35
+ } else {
36
+ fail(red("Invalid ai-agent.json"));
37
+ }
38
+
39
+ if (res.errors?.length) {
40
+ nl();
41
+ for (const e of res.errors) {
42
+ console.log(` ${red("✗")} ${e.path}: ${e.message}`);
43
+ }
44
+ }
45
+ if (res.warnings?.length) {
46
+ nl();
47
+ for (const w of res.warnings) {
48
+ console.log(` ${yellow("⚠")} ${w.path}: ${w.message}`);
49
+ }
50
+ }
51
+ nl();
52
+ }
@@ -0,0 +1,32 @@
1
+ import { getApiKey, getDomain, configPath } from "../config.js";
2
+ import { get } from "../api.js";
3
+ import { heading, success, fail, info, dim, bold, cyan, nl, badgeList } from "../ui.js";
4
+
5
+ export default async function whoami() {
6
+ const key = getApiKey();
7
+ const domain = getDomain();
8
+
9
+ heading("whoami");
10
+ nl();
11
+
12
+ if (!key) {
13
+ fail("Not logged in. Run `aiia login` or `aiia register`.");
14
+ nl();
15
+ return;
16
+ }
17
+
18
+ info(`API key: ${dim(key.slice(0, 12) + "..." + key.slice(-4))}`);
19
+ info(`Config: ${dim(configPath())}`);
20
+
21
+ if (domain) {
22
+ info(`Domain: ${cyan(domain)}`);
23
+ const data = await get(`/api/registry/${domain}`);
24
+ if (data && !data.error) {
25
+ info(`Name: ${bold(data.name)}`);
26
+ info(`Verified: ${data.verified ? "✓ yes" : "✗ no"}`);
27
+ info(`Trust: ${data.trustScore || 0}/100`);
28
+ if (data.badges?.length) info(`Badges: ${data.badges.map((b) => `${b.icon} ${b.name}`).join(" ")}`);
29
+ }
30
+ }
31
+ nl();
32
+ }
package/src/config.js ADDED
@@ -0,0 +1,31 @@
1
+ import { homedir } from "node:os";
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ const CONFIG_DIR = join(homedir(), ".aiia");
6
+ const CONFIG_FILE = join(CONFIG_DIR, "config.json");
7
+
8
+ export function load() {
9
+ try {
10
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
11
+ } catch {
12
+ return {};
13
+ }
14
+ }
15
+
16
+ export function save(config) {
17
+ mkdirSync(CONFIG_DIR, { recursive: true });
18
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2) + "\n");
19
+ }
20
+
21
+ export function getApiKey() {
22
+ return load().api_key || null;
23
+ }
24
+
25
+ export function getDomain() {
26
+ return load().domain || null;
27
+ }
28
+
29
+ export function configPath() {
30
+ return CONFIG_FILE;
31
+ }
package/src/index.js ADDED
@@ -0,0 +1,82 @@
1
+ import { bold, dim, cyan, nl, err } from "./ui.js";
2
+
3
+ const COMMANDS = {
4
+ register: () => import("./commands/register.js"),
5
+ login: () => import("./commands/login.js"),
6
+ whoami: () => import("./commands/whoami.js"),
7
+ search: () => import("./commands/search.js"),
8
+ info: () => import("./commands/info.js"),
9
+ leaderboard: () => import("./commands/leaderboard.js"),
10
+ check: () => import("./commands/check.js"),
11
+ trust: () => import("./commands/trust.js"),
12
+ endorse: () => import("./commands/endorse.js"),
13
+ review: () => import("./commands/review.js"),
14
+ badges: () => import("./commands/badges.js"),
15
+ validate: () => import("./commands/validate.js"),
16
+ monitor: () => import("./commands/monitor.js"),
17
+ chat: () => import("./commands/chat.js"),
18
+ key: () => import("./commands/key.js"),
19
+ };
20
+
21
+ function showHelp() {
22
+ nl();
23
+ console.log(` ${bold("aiia")} ${dim("— CLI for the agentic web")}`);
24
+ nl();
25
+ console.log(` ${bold("Registry")}`);
26
+ console.log(` ${cyan("register")} Register your agent`);
27
+ console.log(` ${cyan("search")} ${dim("[query]")} Search agents`);
28
+ console.log(` ${cyan("info")} ${dim("<domain>")} Agent profile + trust + badges`);
29
+ console.log(` ${cyan("leaderboard")} Top agents by trust score`);
30
+ nl();
31
+ console.log(` ${bold("Trust")}`);
32
+ console.log(` ${cyan("trust")} ${dim("<domain>")} Trust score breakdown`);
33
+ console.log(` ${cyan("endorse")} ${dim("<domain>")} Endorse an agent`);
34
+ console.log(` ${cyan("review")} ${dim("<domain>")} Review an agent (1-5 stars)`);
35
+ console.log(` ${cyan("badges")} ${dim("<domain>")} Show earned badges`);
36
+ console.log(` ${cyan("monitor")} ${dim("<domain>")} Uptime + response times`);
37
+ nl();
38
+ console.log(` ${bold("Tools")}`);
39
+ console.log(` ${cyan("check")} ${dim("<url>")} Run AgentReady audit (15 checks)`);
40
+ console.log(` ${cyan("validate")} ${dim("[file]")} Validate ai-agent.json`);
41
+ nl();
42
+ console.log(` ${bold("Chat")}`);
43
+ console.log(` ${cyan("chat send")} ${dim("<domain> <msg>")} Send encrypted message`);
44
+ console.log(` ${cyan("chat inbox")} Read inbox`);
45
+ console.log(` ${cyan("chat read")} ${dim("<id>")} Read message`);
46
+ console.log(` ${cyan("chat conversations")} List conversations`);
47
+ nl();
48
+ console.log(` ${bold("Auth")}`);
49
+ console.log(` ${cyan("login")} Set API key`);
50
+ console.log(` ${cyan("whoami")} Show current identity`);
51
+ console.log(` ${cyan("key regenerate")} Get new API key`);
52
+ nl();
53
+ console.log(` ${dim("https://aiia.ro")}`);
54
+ nl();
55
+ }
56
+
57
+ export async function run(args) {
58
+ const cmd = args[0];
59
+ const rest = args.slice(1);
60
+
61
+ if (!cmd || cmd === "--help" || cmd === "-h" || cmd === "help") {
62
+ showHelp();
63
+ return;
64
+ }
65
+
66
+ if (cmd === "--version" || cmd === "-v") {
67
+ console.log("aiia 1.0.0");
68
+ return;
69
+ }
70
+
71
+ const loader = COMMANDS[cmd];
72
+ if (!loader) {
73
+ err(`Unknown command: ${cmd}\n Run ${cyan("aiia --help")} for available commands.`);
74
+ }
75
+
76
+ try {
77
+ const mod = await loader();
78
+ await mod.default(rest);
79
+ } catch (e) {
80
+ err(e.message || "Command failed");
81
+ }
82
+ }
package/src/ui.js ADDED
@@ -0,0 +1,82 @@
1
+ import { styleText } from "node:util";
2
+
3
+ const isTTY = process.stdout.isTTY;
4
+
5
+ // Colors
6
+ export const green = (s) => isTTY ? styleText("green", s) : s;
7
+ export const red = (s) => isTTY ? styleText("red", s) : s;
8
+ export const yellow = (s) => isTTY ? styleText("yellow", s) : s;
9
+ export const cyan = (s) => isTTY ? styleText("cyan", s) : s;
10
+ export const bold = (s) => isTTY ? styleText("bold", s) : s;
11
+ export const dim = (s) => isTTY ? styleText("dim", s) : s;
12
+
13
+ // Status messages
14
+ export const success = (msg) => console.log(` ${green("✓")} ${msg}`);
15
+ export const fail = (msg) => console.log(` ${red("✗")} ${msg}`);
16
+ export const info = (msg) => console.log(` ${cyan("ℹ")} ${msg}`);
17
+ export const warn = (msg) => console.log(` ${yellow("⚠")} ${msg}`);
18
+ export const err = (msg) => { console.error(`\n ${red("error:")} ${msg}\n`); process.exit(1); };
19
+
20
+ // Heading
21
+ export const heading = (title, subtitle) => {
22
+ console.log();
23
+ console.log(` ${bold(title)}`);
24
+ if (subtitle) console.log(` ${dim(subtitle)}`);
25
+ };
26
+
27
+ // Spinner
28
+ const FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
29
+ export function spinner(msg) {
30
+ if (!isTTY) { console.log(` ${msg}`); return { stop: () => {}, update: () => {} }; }
31
+ let i = 0;
32
+ const id = setInterval(() => {
33
+ process.stdout.write(`\r ${cyan(FRAMES[i++ % FRAMES.length])} ${msg}`);
34
+ }, 80);
35
+ return {
36
+ stop: (finalMsg) => {
37
+ clearInterval(id);
38
+ process.stdout.write(`\r ${finalMsg || msg}${" ".repeat(20)}\n`);
39
+ },
40
+ update: (newMsg) => { msg = newMsg; },
41
+ };
42
+ }
43
+
44
+ // Trust bar
45
+ export function trustBar(value, max = 100, width = 10) {
46
+ const filled = Math.round((value / max) * width);
47
+ const bar = "█".repeat(Math.max(0, filled)) + "░".repeat(Math.max(0, width - filled));
48
+ const color = value / max >= 0.7 ? green : value / max >= 0.5 ? yellow : red;
49
+ return color(bar);
50
+ }
51
+
52
+ // Stars
53
+ export function stars(rating, max = 5) {
54
+ const full = Math.round(rating);
55
+ return yellow("★".repeat(full)) + dim("☆".repeat(max - full));
56
+ }
57
+
58
+ // Table
59
+ export function table(headers, rows) {
60
+ const widths = headers.map((h, i) =>
61
+ Math.max(h.length, ...rows.map((r) => String(r[i] || "").length))
62
+ );
63
+ const sep = " ";
64
+
65
+ console.log();
66
+ console.log(
67
+ sep + headers.map((h, i) => dim(h.toUpperCase().padEnd(widths[i]))).join(" ")
68
+ );
69
+ for (const row of rows) {
70
+ console.log(
71
+ sep + row.map((cell, i) => String(cell || "").padEnd(widths[i])).join(" ")
72
+ );
73
+ }
74
+ }
75
+
76
+ // Badge display
77
+ export function badgeList(badges) {
78
+ return badges.filter((b) => b.earned).map((b) => `${b.icon} ${b.name}`).join(" ");
79
+ }
80
+
81
+ // Blank line
82
+ export const nl = () => console.log();