File size: 5,752 Bytes
64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 4b88879 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 5cdc985 64bc9a3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 | // HTTP API client β direct communication with the mona.expert control plane.
// Works alongside the WebSocket control channel. Use for:
// - Sending chat messages and getting responses
// - Executing tools directly
// - Testing connectivity
// - Managing agent registration
//
// Supports both sngine-based (agent.mona.expert) and Docker-based platforms.
import { CLOUD, DEFAULTS } from './config.js';
import { log } from './log.js';
const UA = `mona-agent/${DEFAULTS.version}`;
const P = CLOUD.paths; // platform-aware API paths
// ββ Generic fetch with auth βββββββββββββββββββββββββββββββββββββββ
async function apiFetch(apiKey, path, { method = 'GET', body, signal, headers: extraHeaders } = {}) {
const url = CLOUD.base + path;
const headers = {
'Authorization': `Bearer ${apiKey}`,
'content-type': 'application/json',
'user-agent': UA,
'x-mona-agent': DEFAULTS.version,
...(extraHeaders || {}),
};
const res = await fetch(url, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
signal,
});
if (!res.ok) {
let text;
try { text = await res.text(); } catch { text = ''; }
const err = new Error(`API ${res.status}: ${text.slice(0, 200)}`);
err.status = res.status;
throw err;
}
return res;
}
// ββ Health check ββββββββββββββββββββββββββββββββββββββββββββββββββ
export async function checkHealth(apiKey) {
try {
const res = await apiFetch(apiKey, P.health);
const data = await res.json();
// Normalize response
return {
ok: true,
uptime: data.uptime || data.ok ? 1 : 0,
platform: CLOUD.platform,
...data,
};
} catch (err) {
return { ok: false, error: err.message, platform: CLOUD.platform };
}
}
// ββ Verify API key ββββββββββββββββββββββββββββββββββββββββββββββββ
export async function verifyKey(apiKey) {
const res = await apiFetch(apiKey, P.verifyKey, { method: 'GET' });
return res.json();
}
// ββ Send chat message βββββββββββββββββββββββββββββββββββββββββββββ
export async function sendChat(apiKey, agentId, message) {
const res = await apiFetch(apiKey, P.chat(agentId), {
method: 'POST',
body: { message },
});
return res.json();
}
// ββ Execute tool directly via API βββββββββββββββββββββββββββββββββ
export async function execTool(apiKey, agentId, tool, args) {
const res = await apiFetch(apiKey, P.toolExec(agentId), {
method: 'POST',
body: { tool, args },
});
return res.json();
}
// ββ List agents βββββββββββββββββββββββββββββββββββββββββββββββββββ
export async function listAgents(apiKey) {
// Sngine returns array directly, Docker returns { ok: true, agents: [...] }
const res = await apiFetch(apiKey, P.agents);
const data = await res.json();
if (Array.isArray(data)) return data;
return data.agents || data.agent || data;
}
// ββ Get agent status ββββββββββββββββββββββββββββββββββββββββββββββ
export async function getAgent(apiKey, agentId) {
const res = await apiFetch(apiKey, P.agents + '/' + agentId);
return res.json();
}
// ββ Stream reasoning from cloud (SSE) βββββββββββββββββββββββββββββ
export async function think({ apiKey, messages, tools, onChunk, onUsage, signal }) {
const res = await apiFetch(apiKey, P.think, {
method: 'POST',
body: { messages, tools, stream: true },
});
const reader = res.body.getReader();
const dec = new TextDecoder();
let buf = '';
let full = '';
try {
while (true) {
const { value, done } = await reader.read();
if (done) break;
buf += dec.decode(value, { stream: true });
const lines = buf.split('\n');
buf = lines.pop() || '';
for (const line of lines) {
if (!line.startsWith('data:')) continue;
const payload = line.slice(5).trim();
if (payload === '[DONE]') return full;
try {
const j = JSON.parse(payload);
if (j.delta) {
full += j.delta;
onChunk?.(j.delta);
}
if (j.usage) onUsage?.(j.usage);
} catch {
// Skip malformed keepalive lines
}
}
}
} finally {
reader.releaseLock();
}
return full;
}
// ββ Force connection test βββββββββββββββββββββββββββββββββββββββββ
export async function testConnection(apiKey, targetUrl) {
// Override base temporarily if targetUrl provided
const base = targetUrl || CLOUD.base;
const results = {};
try {
log.info(`Testing ${base}...`);
// 1. Health check
results.health = await checkHealth(apiKey);
// 2. Verify auth
try {
results.auth = await verifyKey(apiKey);
} catch (err) {
results.auth = { error: err.message };
}
// 3. List agents
try {
const agents = await listAgents(apiKey);
results.agents = agents;
} catch (err) {
results.agents = { error: err.message };
}
} catch (err) {
results.error = err.message;
}
return results;
}
|