@pompeii-labs/cli
Version:
Magma CLI
317 lines (316 loc) • 8.91 kB
JavaScript
import chalk from "chalk";
import { getAuthSession, storeAuthSession } from "./auth.js";
const ENDPOINT = "https://api.magmadeploy.com/v1";
const FRONTEND = "https://magmadeploy.com";
const endpoint = (path, frontend = false) => `${frontend ? FRONTEND : ENDPOINT}${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 createOrUpdateAgent(options) {
let response;
if (options.id) {
response = await authenticatedFetch(endpoint(`/agents/${options.id}`), {
method: "PUT",
headers: { "Content-Type": "application/json", "x-org-id": options.org_id.toString() },
body: JSON.stringify(options)
});
} else {
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 startAgent(id) {
const response = await authenticatedFetch(endpoint(`/agents/${id}/up`), {
method: "POST"
});
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 exchangeGitHubCode(code) {
const response = await authenticatedFetch(endpoint("/integrations/github"), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ code })
});
if (!response.ok) {
throw await response.json();
}
}
async function listGithubRepos() {
const response = await authenticatedFetch(endpoint("/integrations/github/repos"));
const data = await response.json();
return data;
}
async function linkGithubRepo(repo, agentId) {
const response = await authenticatedFetch(endpoint(`/integrations/github/link`), {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ repo, agentId })
});
if (!response.ok) {
throw await response.json();
}
}
async function getOrgs() {
const response = await authenticatedFetch(endpoint("/orgs"));
const data = await response.json();
return data;
}
async function getAgent(name, orgId) {
const response = await authenticatedFetch(endpoint(`/agents?name=${name}&orgId=${orgId}`));
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 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 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,
createOrUpdateAgent,
deployAgent,
deployTemplate,
endpoint,
exchangeCode,
exchangeGitHubCode,
getAgent,
getEnv,
getOrgs,
getTemplate,
getTemplateByName,
getTemplateEnv,
linkGithubRepo,
listGithubRepos,
listTemplates,
refreshTokens,
setEnv,
setTemplateEnv,
startAgent,
stopAgent
};