Files
vibn-agent-runner/dist/tools/coolify.js
mawkone e91e5e0e37 refactor: split tools.ts into registry-based domain files
Replaces the single 800-line tools.ts and its switch dispatcher with a
Theia-inspired registry pattern — each tool domain is its own file, and
dispatch is a plain Map.get() call with no central routing function.

New structure in src/tools/:
  registry.ts   — ToolDefinition (with handler), registerTool(), executeTool(), ALL_TOOLS
  context.ts    — ToolContext, MemoryUpdate interfaces
  security.ts   — PROTECTED_* constants + assertGiteaWritable/assertCoolifyDeployable
  utils.ts      — safeResolve(), EXCLUDED set
  file.ts       — read_file, write_file, replace_in_file, list_directory, find_files, search_code
  shell.ts      — execute_command
  git.ts        — git_commit_and_push
  coolify.ts    — coolify_*, list_all_apps, get_app_status, deploy_app
  gitea.ts      — gitea_*, list_repos, list_all_issues, read_repo_file
  agent.ts      — spawn_agent, get_job_status
  memory.ts     — save_memory
  index.ts      — barrel with side-effect imports + re-exports

Adding a new tool now requires only a new file + registerTool() call.
No switch statement, no shared array to edit. External API unchanged.

Made-with: Cursor
2026-03-01 15:27:29 -08:00

159 lines
6.4 KiB
JavaScript

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const registry_1 = require("./registry");
const security_1 = require("./security");
async function coolifyFetch(path, ctx, method = 'GET', body) {
const res = await fetch(`${ctx.coolify.apiUrl}/api/v1${path}`, {
method,
headers: {
'Authorization': `Bearer ${ctx.coolify.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();
}
(0, registry_1.registerTool)({
name: 'coolify_list_projects',
description: 'List all projects in the Coolify instance. Returns project names and UUIDs.',
parameters: { type: 'object', properties: {} },
async handler(_args, ctx) {
const projects = await coolifyFetch('/projects', ctx);
if (!Array.isArray(projects))
return projects;
return projects.filter((p) => p.uuid !== security_1.PROTECTED_COOLIFY_PROJECT);
}
});
(0, registry_1.registerTool)({
name: 'coolify_list_applications',
description: 'List applications in a Coolify project.',
parameters: {
type: 'object',
properties: {
project_uuid: { type: 'string', description: 'Project UUID from coolify_list_projects' }
},
required: ['project_uuid']
},
async handler(args, ctx) {
const all = await coolifyFetch('/applications', ctx);
if (!Array.isArray(all))
return all;
return all.filter((a) => a.project_uuid === String(args.project_uuid));
}
});
(0, registry_1.registerTool)({
name: 'coolify_deploy',
description: 'Trigger a deployment for a Coolify application.',
parameters: {
type: 'object',
properties: {
application_uuid: { type: 'string', description: 'Application UUID to deploy' }
},
required: ['application_uuid']
},
async handler(args, ctx) {
const appUuid = String(args.application_uuid);
(0, security_1.assertCoolifyDeployable)(appUuid);
const apps = await coolifyFetch('/applications', ctx);
if (Array.isArray(apps)) {
const app = apps.find((a) => a.uuid === appUuid);
if (app?.project_uuid === security_1.PROTECTED_COOLIFY_PROJECT) {
return { error: `SECURITY: App "${appUuid}" belongs to the protected Vibn project. Agents cannot deploy platform apps.` };
}
}
return coolifyFetch(`/applications/${appUuid}/deploy`, ctx, 'POST');
}
});
(0, registry_1.registerTool)({
name: 'coolify_get_logs',
description: 'Get recent deployment logs for a Coolify application.',
parameters: {
type: 'object',
properties: {
application_uuid: { type: 'string', description: 'Application UUID' }
},
required: ['application_uuid']
},
async handler(args, ctx) {
return coolifyFetch(`/applications/${String(args.application_uuid)}/logs?limit=50`, ctx);
}
});
(0, registry_1.registerTool)({
name: 'list_all_apps',
description: 'List all Coolify applications across all projects with their status (running/stopped/error) and domain.',
parameters: { type: 'object', properties: {} },
async handler(_args, ctx) {
const apps = await coolifyFetch('/applications', ctx);
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
}));
}
});
(0, registry_1.registerTool)({
name: 'get_app_status',
description: 'Get the current deployment status and recent logs for a specific Coolify application by name or UUID.',
parameters: {
type: 'object',
properties: {
app_name: { type: 'string', description: 'Application name (e.g. "vibn-frontend") or UUID' }
},
required: ['app_name']
},
async handler(args, ctx) {
const apps = await coolifyFetch('/applications', ctx);
if (!Array.isArray(apps))
return apps;
const appName = String(args.app_name);
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(`/applications/${app.uuid}/logs?limit=20`, ctx);
return { name: app.name, uuid: app.uuid, status: app.status, fqdn: app.fqdn, logs };
}
});
(0, registry_1.registerTool)({
name: 'deploy_app',
description: 'Trigger a Coolify deployment for an app by name. Use after an agent commits code.',
parameters: {
type: 'object',
properties: {
app_name: { type: 'string', description: 'Application name (e.g. "vibn-frontend")' }
},
required: ['app_name']
},
async handler(args, ctx) {
const apps = await coolifyFetch('/applications', ctx);
if (!Array.isArray(apps))
return apps;
const appName = String(args.app_name);
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.`
};
}
const result = await fetch(`${ctx.coolify.apiUrl}/api/v1/deploy?uuid=${app.uuid}&force=false`, {
headers: { 'Authorization': `Bearer ${ctx.coolify.apiToken}` }
});
return result.json();
}
});