119 lines
5.2 KiB
JavaScript
119 lines
5.2 KiB
JavaScript
"use strict";
|
|
// =============================================================================
|
|
// Pure Coolify API — no ToolContext coupling, no registry coupling.
|
|
//
|
|
// Everything in here takes a plain { apiUrl, apiToken } config and calls
|
|
// the Coolify v1 API directly. Security guardrails (PROTECTED_COOLIFY_PROJECT,
|
|
// PROTECTED_COOLIFY_APPS, assertCoolifyDeployable) are enforced inside each
|
|
// function so every caller — in-process tool handler, MCP server, or future
|
|
// direct SDK user — gets the same protection.
|
|
//
|
|
// This is the shared core consumed by:
|
|
// - tools/coolify.ts (in-process registry used by agent-runner loop)
|
|
// - mcp/coolify-server.ts (stdio MCP server exposed to Goose/Claude/Cursor)
|
|
// =============================================================================
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.listProjects = listProjects;
|
|
exports.listApplications = listApplications;
|
|
exports.deploy = deploy;
|
|
exports.getLogs = getLogs;
|
|
exports.listAllApps = listAllApps;
|
|
exports.getAppStatus = getAppStatus;
|
|
exports.deployApp = deployApp;
|
|
const security_1 = require("./security");
|
|
async function coolifyFetch(cfg, path, method = 'GET', body) {
|
|
const res = await fetch(`${cfg.apiUrl}/api/v1${path}`, {
|
|
method,
|
|
headers: {
|
|
'Authorization': `Bearer ${cfg.apiToken}`,
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json'
|
|
},
|
|
body: body ? JSON.stringify(body) : undefined
|
|
});
|
|
if (!res.ok) {
|
|
return { error: `Coolify API error: ${res.status} ${res.statusText}` };
|
|
}
|
|
return res.json();
|
|
}
|
|
// ---------------------------------------------------------------------------
|
|
// Public API — each function corresponds 1:1 with a registered tool today
|
|
// ---------------------------------------------------------------------------
|
|
async function listProjects(cfg) {
|
|
const projects = await coolifyFetch(cfg, '/projects');
|
|
if (!Array.isArray(projects))
|
|
return projects;
|
|
return projects.filter((p) => p.uuid !== security_1.PROTECTED_COOLIFY_PROJECT);
|
|
}
|
|
async function listApplications(cfg, projectUuid) {
|
|
const all = await coolifyFetch(cfg, '/applications');
|
|
if (!Array.isArray(all))
|
|
return all;
|
|
return all.filter((a) => a.project_uuid === projectUuid);
|
|
}
|
|
async function deploy(cfg, applicationUuid) {
|
|
(0, security_1.assertCoolifyDeployable)(applicationUuid);
|
|
const apps = await coolifyFetch(cfg, '/applications');
|
|
if (Array.isArray(apps)) {
|
|
const app = apps.find((a) => a.uuid === applicationUuid);
|
|
if (app?.project_uuid === security_1.PROTECTED_COOLIFY_PROJECT) {
|
|
return {
|
|
error: `SECURITY: App "${applicationUuid}" belongs to the protected Vibn project. Agents cannot deploy platform apps.`
|
|
};
|
|
}
|
|
}
|
|
return coolifyFetch(cfg, `/applications/${applicationUuid}/deploy`, 'POST');
|
|
}
|
|
async function getLogs(cfg, applicationUuid, limit = 50) {
|
|
return coolifyFetch(cfg, `/applications/${applicationUuid}/logs?limit=${limit}`);
|
|
}
|
|
async function listAllApps(cfg) {
|
|
const apps = await coolifyFetch(cfg, '/applications');
|
|
if (!Array.isArray(apps))
|
|
return apps;
|
|
return apps
|
|
.filter((a) => a.project_uuid !== security_1.PROTECTED_COOLIFY_PROJECT && !security_1.PROTECTED_COOLIFY_APPS.has(a.uuid))
|
|
.map((a) => ({
|
|
uuid: a.uuid,
|
|
name: a.name,
|
|
fqdn: a.fqdn,
|
|
status: a.status,
|
|
repo: a.git_repository,
|
|
branch: a.git_branch
|
|
}));
|
|
}
|
|
async function getAppStatus(cfg, appName) {
|
|
const apps = await coolifyFetch(cfg, '/applications');
|
|
if (!Array.isArray(apps))
|
|
return apps;
|
|
const app = apps.find((a) => a.name?.toLowerCase() === appName.toLowerCase() || a.uuid === appName);
|
|
if (!app)
|
|
return { error: `App "${appName}" not found` };
|
|
if (security_1.PROTECTED_COOLIFY_APPS.has(app.uuid) || app.project_uuid === security_1.PROTECTED_COOLIFY_PROJECT) {
|
|
return {
|
|
error: `SECURITY: "${appName}" is a protected Vibn platform app. Status is not exposed to agents.`
|
|
};
|
|
}
|
|
const logs = await coolifyFetch(cfg, `/applications/${app.uuid}/logs?limit=20`);
|
|
return { name: app.name, uuid: app.uuid, status: app.status, fqdn: app.fqdn, logs };
|
|
}
|
|
async function deployApp(cfg, appName) {
|
|
const apps = await coolifyFetch(cfg, '/applications');
|
|
if (!Array.isArray(apps))
|
|
return apps;
|
|
const app = apps.find((a) => a.name?.toLowerCase() === appName.toLowerCase() || a.uuid === appName);
|
|
if (!app)
|
|
return { error: `App "${appName}" not found` };
|
|
if (security_1.PROTECTED_COOLIFY_APPS.has(app.uuid) || app.project_uuid === security_1.PROTECTED_COOLIFY_PROJECT) {
|
|
return {
|
|
error: `SECURITY: "${appName}" is a protected Vibn platform application. ` +
|
|
`Agents can only deploy user project apps, not platform infrastructure.`
|
|
};
|
|
}
|
|
// Non-project-prefixed deploy endpoint — older Coolify entry point still in use
|
|
const result = await fetch(`${cfg.apiUrl}/api/v1/deploy?uuid=${app.uuid}&force=false`, {
|
|
headers: { 'Authorization': `Bearer ${cfg.apiToken}` }
|
|
});
|
|
return result.json();
|
|
}
|