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
159 lines
6.4 KiB
JavaScript
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();
|
|
}
|
|
});
|