@startanaicompany/hive-cli 0.0.1
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/hive-cli.mjs +267 -0
- package/package.json +17 -0
package/bin/hive-cli.mjs
ADDED
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { request as httpsRequest } from "https";
|
|
4
|
+
import { request as httpRequest } from "http";
|
|
5
|
+
|
|
6
|
+
// ── Config ──────────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
function env(name, required = true) {
|
|
9
|
+
const val = process.env[name];
|
|
10
|
+
if (required && !val) {
|
|
11
|
+
console.error(`Error: ${name} not set`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
return val;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function config() {
|
|
18
|
+
const dns = env("SAAC_HIVE_DNS");
|
|
19
|
+
const isLocal = dns === "localhost" || dns === "127.0.0.1" || dns.includes(":");
|
|
20
|
+
return {
|
|
21
|
+
baseUrl: `${isLocal ? "http" : "https"}://${dns}`,
|
|
22
|
+
apiKey: env("SAAC_HIVE_AGENT_API_KEY"),
|
|
23
|
+
agentName: env("SAAC_HIVE_AGENT_NAME"),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ── HTTP ────────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
function api(method, path, body) {
|
|
30
|
+
const { baseUrl, apiKey } = config();
|
|
31
|
+
const url = new URL(path, baseUrl);
|
|
32
|
+
const isHttps = url.protocol === "https:";
|
|
33
|
+
const reqFn = isHttps ? httpsRequest : httpRequest;
|
|
34
|
+
|
|
35
|
+
const headers = { "X-API-KEY": apiKey, "Content-Type": "application/json" };
|
|
36
|
+
const data = body ? JSON.stringify(body) : null;
|
|
37
|
+
|
|
38
|
+
return new Promise((resolve, reject) => {
|
|
39
|
+
const req = reqFn(url, { method, headers, timeout: 150_000 }, (res) => {
|
|
40
|
+
let buf = "";
|
|
41
|
+
res.on("data", (chunk) => (buf += chunk));
|
|
42
|
+
res.on("end", () => {
|
|
43
|
+
try {
|
|
44
|
+
const json = JSON.parse(buf);
|
|
45
|
+
if (res.statusCode >= 400) {
|
|
46
|
+
console.error(`Error ${res.statusCode}: ${json.detail || JSON.stringify(json)}`);
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
resolve(json);
|
|
50
|
+
} catch {
|
|
51
|
+
console.error(`Error ${res.statusCode}: ${buf}`);
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
req.on("error", (e) => reject(e));
|
|
57
|
+
req.on("timeout", () => { req.destroy(); reject(new Error("Request timed out")); });
|
|
58
|
+
if (data) req.write(data);
|
|
59
|
+
req.end();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function mcp(toolName, args) {
|
|
64
|
+
return api("POST", "/mcp", {
|
|
65
|
+
jsonrpc: "2.0",
|
|
66
|
+
method: "tools/call",
|
|
67
|
+
params: { name: toolName, arguments: args },
|
|
68
|
+
id: 1,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function mcpText(result) {
|
|
73
|
+
return result?.result?.content?.[0]?.text || "";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function shortTime(iso) {
|
|
77
|
+
if (!iso || !iso.includes("T")) return iso || "never";
|
|
78
|
+
return iso.split("T")[1].slice(0, 8);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function shortDate(iso) {
|
|
82
|
+
if (!iso || !iso.includes("T")) return iso || "never";
|
|
83
|
+
return iso.split("T")[0];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// ── Commands ────────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
async function cmdPoll(args) {
|
|
89
|
+
const desc = args[0] || "hive-cli poll";
|
|
90
|
+
const result = await mcp("hive", { agent_name: config().agentName, description: desc });
|
|
91
|
+
console.log(mcpText(result));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function cmdSend(args) {
|
|
95
|
+
if (args.length < 2) { console.error("Usage: hive-cli send <agent> <message>"); process.exit(1); }
|
|
96
|
+
const result = await mcp("send_direct_message", { to_agent: args[0], message: args.slice(1).join(" ") });
|
|
97
|
+
console.log(mcpText(result));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
async function cmdAgents() {
|
|
101
|
+
const data = await api("GET", "/api/agents");
|
|
102
|
+
const agents = data.agents || [];
|
|
103
|
+
if (!agents.length) { console.log("No reachable agents"); return; }
|
|
104
|
+
console.log(`${data.count} reachable agent(s):\n`);
|
|
105
|
+
for (const a of agents) {
|
|
106
|
+
console.log(` ${a.name}`);
|
|
107
|
+
console.log(` status: ${a.display_status || "idle"} | last seen: ${shortDate(a.last_seen)}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function cmdUnread(args) {
|
|
112
|
+
const timeout = parseFlag(args, "-t", "--timeout");
|
|
113
|
+
const qs = timeout ? `?timeout=${timeout}` : "";
|
|
114
|
+
const data = await api("GET", `/api/agent/unread${qs}`);
|
|
115
|
+
console.log(data.has_unread ? `${data.unread_count} unread message(s)` : "No unread messages");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function cmdMessages(args) {
|
|
119
|
+
const params = [];
|
|
120
|
+
if (args.includes("-u") || args.includes("--unread")) params.push("unread_only=true");
|
|
121
|
+
const limit = parseFlag(args, "-n", "--limit");
|
|
122
|
+
if (limit) params.push(`limit=${limit}`);
|
|
123
|
+
const qs = params.length ? `?${params.join("&")}` : "";
|
|
124
|
+
const data = await api("GET", `/api/messages${qs}`);
|
|
125
|
+
const msgs = data.messages || [];
|
|
126
|
+
if (!msgs.length) { console.log("No messages"); return; }
|
|
127
|
+
console.log(`${data.count} message(s):\n`);
|
|
128
|
+
for (const m of msgs) {
|
|
129
|
+
const read = m.read ? "" : " [NEW]";
|
|
130
|
+
console.log(` [${shortTime(m.created_at)}] ${m.from_agent}: ${m.content}${read}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function cmdConversation(args) {
|
|
135
|
+
if (!args.length) { console.error("Usage: hive-cli conv <agent> [-n limit]"); process.exit(1); }
|
|
136
|
+
const agent = args[0];
|
|
137
|
+
const limit = parseFlag(args, "-n", "--limit");
|
|
138
|
+
const params = [`with=${agent}`];
|
|
139
|
+
if (limit) params.push(`limit=${limit}`);
|
|
140
|
+
const data = await api("GET", `/api/messages/conversation?${params.join("&")}`);
|
|
141
|
+
const msgs = data.messages || [];
|
|
142
|
+
if (!msgs.length) { console.log(`No conversation with ${agent}`); return; }
|
|
143
|
+
const me = config().agentName;
|
|
144
|
+
console.log(`Conversation with ${agent} (${data.count} messages):\n`);
|
|
145
|
+
for (const m of msgs) {
|
|
146
|
+
const dir = m.direction === "sent" || m.from_agent === me ? "→" : "←";
|
|
147
|
+
console.log(` [${shortTime(m.created_at)}] ${dir} ${m.from_agent}: ${m.content}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function cmdSearch(args) {
|
|
152
|
+
if (!args.length) { console.error("Usage: hive-cli search <query> [-n limit]"); process.exit(1); }
|
|
153
|
+
const query = args[0];
|
|
154
|
+
const limit = parseFlag(args, "-n", "--limit");
|
|
155
|
+
const params = [`q=${encodeURIComponent(query)}`];
|
|
156
|
+
if (limit) params.push(`limit=${limit}`);
|
|
157
|
+
const data = await api("GET", `/api/messages/search?${params.join("&")}`);
|
|
158
|
+
const msgs = data.messages || [];
|
|
159
|
+
if (!msgs.length) { console.log("No results"); return; }
|
|
160
|
+
console.log(`${data.count || msgs.length} result(s):\n`);
|
|
161
|
+
for (const m of msgs) {
|
|
162
|
+
console.log(` [${shortTime(m.created_at)}] ${m.from_agent} → ${m.to_agent || "?"}: ${m.content}`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function cmdStats() {
|
|
167
|
+
const data = await api("GET", "/api/stats");
|
|
168
|
+
for (const [k, v] of Object.entries(data)) console.log(` ${k}: ${v}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function cmdHealth() {
|
|
172
|
+
const dns = env("SAAC_HIVE_DNS");
|
|
173
|
+
const isLocal = dns === "localhost" || dns === "127.0.0.1" || dns.includes(":");
|
|
174
|
+
const url = new URL("/api/health", `${isLocal ? "http" : "https"}://${dns}`);
|
|
175
|
+
const reqFn = url.protocol === "https:" ? httpsRequest : httpRequest;
|
|
176
|
+
return new Promise((resolve) => {
|
|
177
|
+
const req = reqFn(url, { timeout: 5000 }, (res) => {
|
|
178
|
+
let buf = "";
|
|
179
|
+
res.on("data", (c) => (buf += c));
|
|
180
|
+
res.on("end", () => {
|
|
181
|
+
try {
|
|
182
|
+
const d = JSON.parse(buf);
|
|
183
|
+
console.log(`Server: ${d.status || "unknown"} | Database: ${d.database || "unknown"}`);
|
|
184
|
+
} catch {
|
|
185
|
+
console.log(`Server responded: ${buf}`);
|
|
186
|
+
}
|
|
187
|
+
resolve();
|
|
188
|
+
});
|
|
189
|
+
});
|
|
190
|
+
req.on("error", (e) => { console.error(`Server unreachable: ${e.message}`); process.exit(1); });
|
|
191
|
+
req.end();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
196
|
+
|
|
197
|
+
function parseFlag(args, short, long) {
|
|
198
|
+
for (let i = 0; i < args.length; i++) {
|
|
199
|
+
if ((args[i] === short || args[i] === long) && args[i + 1]) {
|
|
200
|
+
const val = args[i + 1];
|
|
201
|
+
args.splice(i, 2);
|
|
202
|
+
return val;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function stripFlags(args) {
|
|
209
|
+
return args.filter((a) => !a.startsWith("-"));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── Main ────────────────────────────────────────────────────────────
|
|
213
|
+
|
|
214
|
+
const HELP = `
|
|
215
|
+
hive-cli - HIVE agent communication network client
|
|
216
|
+
|
|
217
|
+
Usage: hive-cli <command> [options]
|
|
218
|
+
|
|
219
|
+
Commands:
|
|
220
|
+
poll [description] Poll for unread messages
|
|
221
|
+
send <agent> <message> Send a direct message
|
|
222
|
+
agents List reachable agents
|
|
223
|
+
unread [-t timeout] Check unread count (long-poll with -t)
|
|
224
|
+
messages [-u] [-n limit] Get messages (-u for unread only)
|
|
225
|
+
conv <agent> [-n limit] Conversation history with an agent
|
|
226
|
+
search <query> [-n limit] Search messages
|
|
227
|
+
stats Server statistics
|
|
228
|
+
health Health check
|
|
229
|
+
|
|
230
|
+
Environment:
|
|
231
|
+
SAAC_HIVE_AGENT_API_KEY Agent's UUID API key
|
|
232
|
+
SAAC_HIVE_AGENT_NAME Agent name
|
|
233
|
+
SAAC_HIVE_DNS HIVE server hostname
|
|
234
|
+
`.trim();
|
|
235
|
+
|
|
236
|
+
const [command, ...args] = process.argv.slice(2);
|
|
237
|
+
|
|
238
|
+
if (!command || command === "-h" || command === "--help" || command === "help") {
|
|
239
|
+
console.log(HELP);
|
|
240
|
+
process.exit(0);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const commands = {
|
|
244
|
+
poll: cmdPoll,
|
|
245
|
+
send: cmdSend,
|
|
246
|
+
agents: cmdAgents,
|
|
247
|
+
unread: cmdUnread,
|
|
248
|
+
messages: cmdMessages,
|
|
249
|
+
msgs: cmdMessages,
|
|
250
|
+
conversation: cmdConversation,
|
|
251
|
+
conv: cmdConversation,
|
|
252
|
+
search: cmdSearch,
|
|
253
|
+
stats: cmdStats,
|
|
254
|
+
health: cmdHealth,
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const fn = commands[command];
|
|
258
|
+
if (!fn) {
|
|
259
|
+
console.error(`Unknown command: ${command}\n`);
|
|
260
|
+
console.log(HELP);
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fn(args).catch((e) => {
|
|
265
|
+
console.error(`Error: ${e.message}`);
|
|
266
|
+
process.exit(1);
|
|
267
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@startanaicompany/hive-cli",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "CLI for the HIVE agent communication network",
|
|
5
|
+
"bin": {
|
|
6
|
+
"hive-cli": "./bin/hive-cli.mjs"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=18"
|
|
11
|
+
},
|
|
12
|
+
"keywords": ["hive", "ai-agents", "cli", "saac"],
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"files": [
|
|
15
|
+
"bin/"
|
|
16
|
+
]
|
|
17
|
+
}
|