@pompeii-labs/cli
Version:
Magma CLI
326 lines (325 loc) • 9.02 kB
JavaScript
import chalk from "chalk";
import { getAuthSession, storeAuthSession } from "./auth.js";
const ENDPOINT = "https://api.magmadeploy.com/v1";
const ENDPOINT_NGROK = "https://magma.ngrok.app/v1";
const FRONTEND = "https://magmadeploy.com";
const endpoint = (path, frontend = false) => {
let endpoint2;
if (frontend) {
endpoint2 = FRONTEND;
} else if (process.argv.includes("--ngrok")) {
endpoint2 = ENDPOINT_NGROK;
} else if (process.argv.includes("--endpoint")) {
endpoint2 = process.argv[process.argv.indexOf("--endpoint") + 1];
} else {
endpoint2 = ENDPOINT;
}
return `${endpoint2}${path}`;
};
async function authenticatedFetch(input, init) {
const session = await getAuthSession();
const response = await fetch(input, {
...init,
headers: {
Authorization: `Bearer ${session.access_token}`,
...init?.headers
}
});
if (!response.ok) {
if (response.status === 403) {
const newTokens = await refreshTokens(session.refresh_token);
if (!newTokens) throw new Error("No tokens received from server");
await storeAuthSession(newTokens);
return authenticatedFetch(input, init);
} else if (response.status === 413) {
throw new Error("Payload too large");
}
let error = await response.text();
try {
error = JSON.parse(error).message;
} catch (e) {
}
throw new Error(error);
}
return response;
}
async function getTemplate(id) {
const response = await authenticatedFetch(endpoint(`/templates/${id}`));
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function getTemplateByName(name) {
const response = await authenticatedFetch(endpoint(`/templates?name=${name}`));
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function listTemplates() {
const response = await fetch(endpoint("/templates"));
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function saveAgent(options) {
const response = await authenticatedFetch(endpoint("/agents"), {
method: "POST",
headers: { "Content-Type": "application/json", "x-org-id": options.org_id.toString() },
body: JSON.stringify(options)
});
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function deployAgent(agent) {
const response = await authenticatedFetch(endpoint(`/agents/${agent.id}/deploy`), {
method: "POST",
headers: { "Content-Type": "application/json", "x-org-id": agent.orgId.toString() }
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let isReading = true;
try {
while (isReading) {
const { value, done } = await reader.read();
if (done) {
isReading = false;
break;
}
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n\n");
buffer = lines.pop() || "";
for (const line of lines) {
try {
const cleaned = line.replace("data: ", "").replace("\n", "");
const parsed = JSON.parse(cleaned);
switch (parsed.type) {
case "success":
reader.cancel();
isReading = false;
break;
case "message":
console.log(chalk.dim(parsed.data));
break;
case "error":
reader.cancel();
throw new Error(parsed.data);
default:
break;
}
} catch (error) {
if (error instanceof Error) {
reader.cancel();
throw error;
}
}
}
}
} catch (error) {
reader.cancel();
throw error;
} finally {
reader.cancel();
}
}
async function stopAgent(id) {
const response = await authenticatedFetch(endpoint(`/agents/${id}/deploy`), {
method: "DELETE"
});
if (!response.ok) {
throw await response.json();
}
}
async function exchangeCode(code) {
const response = await fetch(endpoint("/auth/exchange"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code })
});
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function refreshTokens(refresh_token) {
const response = await fetch(endpoint("/auth/refresh"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ refresh_token })
});
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function getOrgs() {
const response = await authenticatedFetch(endpoint("/orgs"));
const data = await response.json();
return data;
}
async function getAgent(name, orgId, version) {
const response = await authenticatedFetch(endpoint(`/agents?name=${name}`), {
headers: { "x-org-id": orgId.toString() }
});
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
if (!data) {
throw new Error("Agent not found");
}
let agent = data;
if (version) {
agent = await getVersionedAgent(agent.id, orgId, version);
}
return agent;
}
async function getVersionedAgent(id, orgId, version) {
const url = new URL(endpoint(`/agents/${id}`));
url.searchParams.set("source", "true");
if (version) {
url.searchParams.set("version", version);
}
const response = await authenticatedFetch(url, {
headers: { "x-org-id": orgId.toString() }
});
if (!response.ok) {
throw await response.json();
}
const data = await response.json();
return data;
}
async function getEnv(agentId, key) {
const response = await authenticatedFetch(endpoint(`/agents/${agentId}/env?key=${key}`));
if (!response.ok) {
throw await response.json();
}
const data = await response.text();
return data;
}
async function setEnv(agentId, pairs) {
const response = await authenticatedFetch(endpoint(`/agents/${agentId}/env`), {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(pairs)
});
if (!response.ok) {
throw await response.json();
}
}
async function unsetEnv(agentId, key) {
const response = await authenticatedFetch(endpoint(`/agents/${agentId}/env/${key}`), {
method: "DELETE"
});
if (!response.ok) {
throw await response.json();
}
}
async function getTemplateEnv(templateId, key) {
const response = await authenticatedFetch(endpoint(`/templates/${templateId}/env?key=${key}`));
if (!response.ok) {
throw await response.json();
}
const data = await response.text();
return data;
}
async function setTemplateEnv(templateId, pairs) {
const response = await authenticatedFetch(endpoint(`/templates/${templateId}/env`), {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(pairs)
});
if (!response.ok) {
throw await response.json();
}
}
async function unsetTemplateEnv(templateId, key) {
const response = await authenticatedFetch(endpoint(`/templates/${templateId}/env/${key}`), {
method: "DELETE"
});
if (!response.ok) {
throw await response.json();
}
}
async function deployTemplate(body) {
const response = await authenticatedFetch(endpoint("/templates"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body)
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let buffer = "";
let isReading = true;
try {
while (isReading) {
const { value, done } = await reader.read();
if (done) {
isReading = false;
break;
}
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split("\n\n");
buffer = lines.pop() || "";
for (const line of lines) {
try {
const cleaned = line.replace("data: ", "").replace("\n", "");
const parsed = JSON.parse(cleaned);
switch (parsed.type) {
case "message":
console.log(chalk.dim(parsed.data));
break;
case "error":
reader.cancel();
throw new Error(parsed.data);
default:
break;
}
} catch (error) {
if (error instanceof Error) {
reader.cancel();
throw error;
}
}
}
}
} catch (error) {
reader.cancel();
throw error;
} finally {
reader.cancel();
}
}
export {
FRONTEND,
authenticatedFetch,
deployAgent,
deployTemplate,
endpoint,
exchangeCode,
getAgent,
getEnv,
getOrgs,
getTemplate,
getTemplateByName,
getTemplateEnv,
listTemplates,
refreshTokens,
saveAgent,
setEnv,
setTemplateEnv,
stopAgent,
unsetEnv,
unsetTemplateEnv
};