@mks2508/coolify-mks-cli-mcp 0.4.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/cli/coolify-state.d.ts +51 -0
  2. package/dist/cli/coolify-state.d.ts.map +1 -0
  3. package/dist/cli/index.js +2862 -631
  4. package/dist/coolify/config.d.ts +1 -1
  5. package/dist/coolify/config.d.ts.map +1 -1
  6. package/dist/coolify/index.d.ts +626 -12
  7. package/dist/coolify/index.d.ts.map +1 -1
  8. package/dist/coolify/types.d.ts +87 -3
  9. package/dist/coolify/types.d.ts.map +1 -1
  10. package/dist/dist-C4hIkHif.js +66 -0
  11. package/dist/dist-C4hIkHif.js.map +1 -0
  12. package/dist/dist-DEPvJhbP.js +3 -0
  13. package/dist/index.cjs +8511 -28542
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.ts +32 -8
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +8470 -28506
  18. package/dist/index.js.map +1 -1
  19. package/dist/network.d.ts +75 -0
  20. package/dist/network.d.ts.map +1 -0
  21. package/dist/sdk.d.ts +356 -0
  22. package/dist/sdk.d.ts.map +1 -0
  23. package/dist/server/index.d.ts +9 -0
  24. package/dist/server/index.d.ts.map +1 -0
  25. package/dist/server/sse.js +3 -1
  26. package/dist/server/stdio.d.ts +0 -2
  27. package/dist/server/stdio.d.ts.map +1 -1
  28. package/dist/server/stdio.js +3307 -1618
  29. package/dist/tools/definitions.d.ts +1 -1
  30. package/dist/tools/definitions.d.ts.map +1 -1
  31. package/dist/tools/handlers.d.ts +6 -7
  32. package/dist/tools/handlers.d.ts.map +1 -1
  33. package/dist/tools/index.d.ts +8 -0
  34. package/dist/tools/index.d.ts.map +1 -0
  35. package/dist/trace.d.ts +71 -0
  36. package/dist/trace.d.ts.map +1 -0
  37. package/dist/utils/format.d.ts +1 -1
  38. package/dist/utils/format.d.ts.map +1 -1
  39. package/package.json +13 -7
  40. package/src/cli/actions.ts +162 -0
  41. package/src/cli/commands/active-deployments.ts +24 -0
  42. package/src/cli/commands/build-logs.ts +58 -0
  43. package/src/cli/commands/cancel-deploy.ts +35 -0
  44. package/src/cli/commands/config.ts +53 -47
  45. package/src/cli/commands/create.ts +74 -53
  46. package/src/cli/commands/databases.ts +63 -0
  47. package/src/cli/commands/db.ts +68 -0
  48. package/src/cli/commands/delete.ts +41 -29
  49. package/src/cli/commands/deploy.ts +42 -21
  50. package/src/cli/commands/deployments.ts +41 -31
  51. package/src/cli/commands/destinations.ts +19 -27
  52. package/src/cli/commands/diagnose.ts +139 -0
  53. package/src/cli/commands/env.ts +66 -41
  54. package/src/cli/commands/environments.ts +36 -32
  55. package/src/cli/commands/exec.ts +39 -0
  56. package/src/cli/commands/keys.ts +46 -0
  57. package/src/cli/commands/list.ts +29 -27
  58. package/src/cli/commands/logs.ts +33 -18
  59. package/src/cli/commands/network.ts +145 -0
  60. package/src/cli/commands/projects.ts +51 -39
  61. package/src/cli/commands/restart.ts +60 -0
  62. package/src/cli/commands/server-resources.ts +71 -0
  63. package/src/cli/commands/servers.ts +23 -23
  64. package/src/cli/commands/service-logs.ts +59 -0
  65. package/src/cli/commands/services.ts +63 -0
  66. package/src/cli/commands/show.ts +72 -41
  67. package/src/cli/commands/start.ts +60 -0
  68. package/src/cli/commands/stop.ts +60 -0
  69. package/src/cli/commands/svc.ts +68 -0
  70. package/src/cli/commands/teams.ts +60 -0
  71. package/src/cli/commands/update.ts +73 -49
  72. package/src/cli/commands/version.ts +37 -0
  73. package/src/cli/coolify-state.ts +88 -0
  74. package/src/cli/index.ts +400 -125
  75. package/src/coolify/config.ts +29 -27
  76. package/src/coolify/index.ts +2221 -371
  77. package/src/coolify/types.ts +218 -123
  78. package/src/index.ts +82 -868
  79. package/src/network.ts +298 -0
  80. package/src/sdk.ts +597 -0
  81. package/src/server/index.ts +13 -0
  82. package/src/server/sse.ts +33 -25
  83. package/src/server/stdio.ts +24 -27
  84. package/src/tools/definitions.ts +893 -264
  85. package/src/tools/handlers.ts +556 -748
  86. package/src/tools/index.ts +8 -0
  87. package/src/trace.ts +116 -0
  88. package/src/utils/format.ts +36 -33
@@ -6,13 +6,15 @@
6
6
  * @module
7
7
  */
8
8
 
9
- import { ok, err, isErr, type Result } from '@mks2508/no-throw'
10
- import { component } from '@mks2508/better-logger'
11
- import { loadConfig, type ICoolifyConfig } from './config.js'
9
+ import { ok, err, isErr, type Result } from "@mks2508/no-throw";
10
+ import { component } from "@mks2508/better-logger";
11
+ import { loadConfig, type ICoolifyConfig } from "./config.js";
12
12
  import {
13
13
  type ICoolifyAppOptions,
14
14
  type ICoolifyAppResult,
15
15
  type ICoolifyApplication,
16
+ type ICoolifyDatabase,
17
+ type ICoolifyDatabaseBackup,
16
18
  type ICoolifyDeleteResult,
17
19
  type ICoolifyDeployment,
18
20
  type ICoolifyDeployOptions,
@@ -21,36 +23,41 @@ import {
21
23
  type ICoolifyEnvironment,
22
24
  type ICoolifyLogs,
23
25
  type ICoolifyLogsOptions,
26
+ type ICoolifyPrivateKey,
24
27
  type ICoolifyProject,
25
28
  type ICoolifyServer,
29
+ type ICoolifyServerDomain,
30
+ type ICoolifyServerResource,
31
+ type ICoolifyService as ICoolifyServiceType,
26
32
  type ICoolifyTeam,
27
33
  type ICoolifyUpdateOptions,
34
+ type ICoolifyVersion,
28
35
  type IProgressCallback,
29
- } from './types.js'
36
+ } from "./types.js";
30
37
 
31
- const log = component('CoolifyService')
38
+ const log = component("CoolifyService");
32
39
 
33
40
  /**
34
41
  * Coolify API response type.
35
42
  */
36
43
  interface ICoolifyApiResponse<T> {
37
- data?: T
38
- error?: string
39
- status: number
40
- durationMs?: number
44
+ data?: T;
45
+ error?: string;
46
+ status: number;
47
+ durationMs?: number;
41
48
  }
42
49
 
43
50
  /**
44
51
  * Environment variable from Coolify API.
45
52
  */
46
53
  export interface ICoolifyEnvVar {
47
- uuid: string
48
- key: string
49
- value: string
50
- real_value?: string
51
- is_buildtime: boolean
52
- is_runtime: boolean
53
- is_required: boolean
54
+ uuid: string;
55
+ key: string;
56
+ value: string;
57
+ real_value?: string;
58
+ is_buildtime: boolean;
59
+ is_runtime: boolean;
60
+ is_required: boolean;
54
61
  }
55
62
 
56
63
  /**
@@ -72,9 +79,9 @@ export interface ICoolifyEnvVar {
72
79
  * ```
73
80
  */
74
81
  export class CoolifyService {
75
- private baseUrl: string | undefined
76
- private token: string | undefined
77
- private config: ICoolifyConfig = {}
82
+ private baseUrl: string | undefined;
83
+ private token: string | undefined;
84
+ private config: ICoolifyConfig = {};
78
85
 
79
86
  /**
80
87
  * Checks if the service is configured with URL and token.
@@ -82,9 +89,9 @@ export class CoolifyService {
82
89
  * @returns true if both URL and token are set
83
90
  */
84
91
  isConfigured(): boolean {
85
- const hasUrl = !!this.baseUrl || !!process.env.COOLIFY_URL
86
- const hasToken = !!this.token || !!process.env.COOLIFY_TOKEN
87
- return hasUrl && hasToken
92
+ const hasUrl = !!this.baseUrl || !!process.env.COOLIFY_URL;
93
+ const hasToken = !!this.token || !!process.env.COOLIFY_TOKEN;
94
+ return hasUrl && hasToken;
88
95
  }
89
96
 
90
97
  /**
@@ -93,31 +100,43 @@ export class CoolifyService {
93
100
  * @returns Result indicating success or error
94
101
  */
95
102
  async init(): Promise<Result<void, Error>> {
96
- const configResult = await loadConfig()
103
+ const configResult = await loadConfig();
97
104
 
98
105
  if (isErr(configResult)) {
99
- log.error('Failed to load config')
100
- return err(configResult.error)
106
+ log.error("Failed to load config");
107
+ return err(configResult.error);
101
108
  }
102
109
 
103
- this.config = configResult.value
104
- this.baseUrl = this.config.url || process.env.COOLIFY_URL
105
- this.token = this.config.token || process.env.COOLIFY_TOKEN
110
+ this.config = configResult.value;
111
+ this.baseUrl = this.config.url || process.env.COOLIFY_URL;
112
+ this.token = this.config.token || process.env.COOLIFY_TOKEN;
106
113
 
107
114
  if (!this.baseUrl) {
108
- log.error('No Coolify URL configured')
109
- log.info('Set COOLIFY_URL environment variable or run: coolify-mcp config set url <url>')
110
- return err(new Error('No Coolify URL configured. Set COOLIFY_URL or use config command.'))
115
+ log.error("No Coolify URL configured");
116
+ log.info(
117
+ "Set COOLIFY_URL environment variable or run: coolify-mcp config set url <url>",
118
+ );
119
+ return err(
120
+ new Error(
121
+ "No Coolify URL configured. Set COOLIFY_URL or use config command.",
122
+ ),
123
+ );
111
124
  }
112
125
 
113
126
  if (!this.token) {
114
- log.error('No Coolify token configured')
115
- log.info('Set COOLIFY_TOKEN environment variable or run: coolify-mcp config set token <token>')
116
- return err(new Error('No Coolify token configured. Set COOLIFY_TOKEN or use config command.'))
127
+ log.error("No Coolify token configured");
128
+ log.info(
129
+ "Set COOLIFY_TOKEN environment variable or run: coolify-mcp config set token <token>",
130
+ );
131
+ return err(
132
+ new Error(
133
+ "No Coolify token configured. Set COOLIFY_TOKEN or use config command.",
134
+ ),
135
+ );
117
136
  }
118
137
 
119
- log.debug('Coolify connection configured')
120
- return ok(undefined)
138
+ log.debug("Coolify connection configured");
139
+ return ok(undefined);
121
140
  }
122
141
 
123
142
  /**
@@ -129,57 +148,70 @@ export class CoolifyService {
129
148
  */
130
149
  private async request<T>(
131
150
  endpoint: string,
132
- options: RequestInit = {}
151
+ options: RequestInit = {},
133
152
  ): Promise<ICoolifyApiResponse<T>> {
134
- const startTime = Date.now()
153
+ const startTime = Date.now();
135
154
 
136
155
  if (!this.baseUrl || !this.token) {
137
- return { error: 'Coolify not configured', status: 0, durationMs: Date.now() - startTime }
156
+ return {
157
+ error: "Coolify not configured",
158
+ status: 0,
159
+ durationMs: Date.now() - startTime,
160
+ };
138
161
  }
139
162
 
140
163
  try {
141
- const baseUrl = this.baseUrl.replace(/\/+$/, '')
142
- const url = `${baseUrl}/api/v1${endpoint}`
164
+ const baseUrl = this.baseUrl.replace(/\/+$/, "");
165
+ const url = `${baseUrl}/api/v1${endpoint}`;
143
166
 
144
167
  const response = await fetch(url, {
145
168
  ...options,
146
169
  headers: {
147
170
  Authorization: `Bearer ${this.token}`,
148
- 'Content-Type': 'application/json',
149
- Accept: 'application/json',
171
+ "Content-Type": "application/json",
172
+ Accept: "application/json",
150
173
  ...options.headers,
151
174
  },
152
- })
175
+ });
153
176
 
154
- const text = await response.text()
155
- const durationMs = Date.now() - startTime
156
- let data: T | undefined
177
+ const text = await response.text();
178
+ const durationMs = Date.now() - startTime;
179
+ let data: T | undefined;
157
180
 
158
181
  try {
159
- data = text ? JSON.parse(text) : undefined
182
+ data = text ? JSON.parse(text) : undefined;
160
183
  } catch {
161
184
  if (!response.ok) {
162
- return { error: text || `HTTP ${response.status}`, status: response.status, durationMs }
185
+ return {
186
+ error: text || `HTTP ${response.status}`,
187
+ status: response.status,
188
+ durationMs,
189
+ };
163
190
  }
164
191
  }
165
192
 
166
193
  if (!response.ok) {
167
- const parsed = data as { message?: string; errors?: Record<string, string[]> } | undefined
168
- let errorMessage = parsed?.message || `HTTP ${response.status}`
194
+ const parsed = data as
195
+ | { message?: string; errors?: Record<string, string[]> }
196
+ | undefined;
197
+ let errorMessage = parsed?.message || `HTTP ${response.status}`;
169
198
  // Include validation errors if present (Coolify returns { message, errors: { field: [reasons] } })
170
199
  if (parsed?.errors) {
171
200
  const details = Object.entries(parsed.errors)
172
- .map(([field, reasons]) => `${field}: ${Array.isArray(reasons) ? reasons.join(', ') : String(reasons)}`)
173
- .join('; ')
174
- errorMessage += ` ${details}`
201
+ .map(
202
+ ([field, reasons]) =>
203
+ `${field}: ${Array.isArray(reasons) ? reasons.join(", ") : String(reasons)}`,
204
+ )
205
+ .join("; ");
206
+ errorMessage += ` — ${details}`;
175
207
  }
176
- return { error: errorMessage, status: response.status, durationMs }
208
+ return { error: errorMessage, status: response.status, durationMs };
177
209
  }
178
210
 
179
- return { data, status: response.status, durationMs }
211
+ return { data, status: response.status, durationMs };
180
212
  } catch (error) {
181
- const message = error instanceof Error ? error.message : 'Unknown error'
182
- return { error: message, status: 0, durationMs: Date.now() - startTime }
213
+ const message = error instanceof Error ? error.message : "Unknown error";
214
+ return { error: message, status: 0, durationMs: Date.now() - startTime };
183
215
  }
184
216
  }
185
217
 
@@ -192,61 +224,63 @@ export class CoolifyService {
192
224
  */
193
225
  async deploy(
194
226
  options: ICoolifyDeployOptions,
195
- onProgress?: IProgressCallback
227
+ onProgress?: IProgressCallback,
196
228
  ): Promise<Result<ICoolifyDeployResult, Error>> {
197
229
  if (!options.uuid && !options.tag) {
198
- return err(new Error('Either uuid or tag is required'))
230
+ return err(new Error("Either uuid or tag is required"));
199
231
  }
200
232
 
201
- const appId = options.uuid?.slice(0, 8) || options.tag || 'unknown'
202
- onProgress?.(5, `Preparing deployment for ${appId}...`)
233
+ const appId = options.uuid?.slice(0, 8) || options.tag || "unknown";
234
+ onProgress?.(5, `Preparing deployment for ${appId}...`);
203
235
 
204
- log.info(`Deploying application ${options.uuid || options.tag}`)
236
+ log.info(`Deploying application ${options.uuid || options.tag}`);
205
237
 
206
- onProgress?.(25, 'Validating deployment configuration')
207
- onProgress?.(50, 'Triggering build pipeline...')
238
+ onProgress?.(25, "Validating deployment configuration");
239
+ onProgress?.(50, "Triggering build pipeline...");
208
240
 
209
241
  // Build query parameters for deploy endpoint
210
- const params = new URLSearchParams()
211
- if (options.uuid) params.set('uuid', options.uuid)
212
- if (options.tag) params.set('tag', options.tag)
213
- if (options.force) params.set('force', 'true')
242
+ const params = new URLSearchParams();
243
+ if (options.uuid) params.set("uuid", options.uuid);
244
+ if (options.tag) params.set("tag", options.tag);
245
+ if (options.force) params.set("force", "true");
214
246
 
215
- const endpoint = `/deploy${params.toString() ? `?${params.toString()}` : ''}`
247
+ const endpoint = `/deploy${params.toString() ? `?${params.toString()}` : ""}`;
216
248
 
217
249
  const result = await this.request<{
218
250
  deployments: Array<{
219
- message: string
220
- resource_uuid: string
221
- deployment_uuid: string
222
- }>
251
+ message: string;
252
+ resource_uuid: string;
253
+ deployment_uuid: string;
254
+ }>;
223
255
  }>(endpoint, {
224
- method: 'POST',
225
- })
256
+ method: "GET",
257
+ });
226
258
 
227
259
  if (result.error) {
228
- log.error(`Deployment failed: ${result.error}`)
229
- return err(new Error(result.error))
260
+ log.error(`Deployment failed: ${result.error}`);
261
+ return err(new Error(result.error));
230
262
  }
231
263
 
232
264
  // Response is { deployments: [{ message, resource_uuid, deployment_uuid }] }
233
- const deployments = result.data?.deployments || []
265
+ const deployments = result.data?.deployments || [];
234
266
  if (deployments.length === 0) {
235
- log.error('No deployments started')
236
- return err(new Error('No deployments started - check application configuration'))
267
+ log.error("No deployments started");
268
+ return err(
269
+ new Error("No deployments started - check application configuration"),
270
+ );
237
271
  }
238
272
 
239
- const deployment = deployments[0]
273
+ const deployment = deployments[0];
240
274
 
241
- onProgress?.(90, 'Build started on Coolify server')
242
- onProgress?.(100, 'Deployment triggered')
275
+ onProgress?.(90, "Build started on Coolify server");
276
+ onProgress?.(100, "Deployment triggered");
243
277
 
244
- log.success(`Deployment started: ${deployment.deployment_uuid}`)
278
+ log.success(`Deployment started: ${deployment.deployment_uuid}`);
245
279
  return ok({
246
280
  success: true,
247
281
  deploymentUuid: deployment.deployment_uuid,
248
282
  resourceUuid: deployment.resource_uuid,
249
- })
283
+ });
250
284
  }
251
285
 
252
286
  /**
@@ -266,27 +300,29 @@ export class CoolifyService {
266
300
  */
267
301
  async createApplication(
268
302
  options: ICoolifyAppOptions,
269
- onProgress?: IProgressCallback
303
+ onProgress?: IProgressCallback,
270
304
  ): Promise<Result<ICoolifyAppResult, Error>> {
271
- onProgress?.(5, `Preparing app "${options.name}"`)
305
+ onProgress?.(5, `Preparing app "${options.name}"`);
272
306
 
273
- const appType = options.type || 'public'
274
- log.info(`Creating application ${options.name} (type: ${appType})`)
307
+ const appType = options.type || "public";
308
+ log.info(`Creating application ${options.name} (type: ${appType})`);
275
309
 
276
- onProgress?.(25, `Validating server ${options.serverUuid.slice(0, 8)}...`)
277
- onProgress?.(50, 'Sending creation request to Coolify API...')
310
+ onProgress?.(25, `Validating server ${options.serverUuid.slice(0, 8)}...`);
311
+ onProgress?.(50, "Sending creation request to Coolify API...");
278
312
 
279
313
  // Determine endpoint based on application type
280
314
  const endpointMap: Record<string, string> = {
281
- 'public': '/applications/public',
282
- 'private-github-app': '/applications/private-github-app',
283
- 'private-deploy-key': '/applications/private-deploy-key',
284
- 'dockerfile': '/applications/dockerfile',
285
- 'docker-image': '/applications/docker-image',
286
- 'docker-compose': '/applications/docker-compose',
287
- }
288
-
289
- const endpoint = endpointMap[appType] || '/applications/public'
315
+ public: "/applications/public",
316
+ "private-github-app": "/applications/private-github-app",
317
+ "private-deploy-key": "/applications/private-deploy-key",
318
+ dockerfile: "/applications/dockerfile",
319
+ "docker-image": "/applications/docker-image",
320
+ "docker-compose": "/applications/docker-compose",
321
+ dockerimage: "/applications/dockerimage",
322
+ dockercompose: "/applications/dockercompose",
323
+ };
324
+
325
+ const endpoint = endpointMap[appType] || "/applications/public";
290
326
 
291
327
  // Build request body based on application type
292
328
  const body: Record<string, unknown> = {
@@ -295,60 +331,64 @@ export class CoolifyService {
295
331
  project_uuid: options.projectUuid,
296
332
  environment_uuid: options.environmentUuid,
297
333
  server_uuid: options.serverUuid,
298
- }
334
+ };
299
335
 
300
336
  // Type-specific fields
301
- if (appType === 'public' || appType === 'private-github-app' || appType === 'private-deploy-key') {
337
+ if (
338
+ appType === "public" ||
339
+ appType === "private-github-app" ||
340
+ appType === "private-deploy-key"
341
+ ) {
302
342
  if (options.githubRepoUrl) {
303
343
  // Coolify expects 'user/repo' format, not full URL
304
344
  body.git_repository = options.githubRepoUrl
305
- .replace(/^https?:\/\/github\.com\//, '')
306
- .replace(/\.git$/, '')
345
+ .replace(/^https?:\/\/github\.com\//, "")
346
+ .replace(/\.git$/, "");
307
347
  }
308
348
  if (options.githubAppUuid) {
309
- body.github_app_uuid = options.githubAppUuid
349
+ body.github_app_uuid = options.githubAppUuid;
310
350
  }
311
- body.git_branch = options.branch || 'main'
312
- body.build_pack = options.buildPack || 'dockerfile'
351
+ body.git_branch = options.branch || "main";
352
+ body.build_pack = options.buildPack || "dockerfile";
313
353
  if (options.portsExposes) {
314
- body.ports_exposes = options.portsExposes
354
+ body.ports_exposes = options.portsExposes;
315
355
  }
316
356
  // Dockerfile / Docker Compose configuration
317
357
  if (options.dockerfileLocation) {
318
- body.dockerfile_location = options.dockerfileLocation
358
+ body.dockerfile_location = options.dockerfileLocation;
319
359
  }
320
360
  if (options.dockerComposeLocation) {
321
- body.docker_compose_location = options.dockerComposeLocation
361
+ body.docker_compose_location = options.dockerComposeLocation;
322
362
  }
323
363
  if (options.baseDirectory) {
324
- body.base_directory = options.baseDirectory
364
+ body.base_directory = options.baseDirectory;
325
365
  }
326
- } else if (appType === 'docker-image' && options.dockerImage) {
327
- body.docker_image = options.dockerImage
328
- } else if (appType === 'docker-compose' && options.dockerCompose) {
329
- body.docker_compose = options.dockerCompose
366
+ } else if (appType === "docker-image" && options.dockerImage) {
367
+ body.docker_image = options.dockerImage;
368
+ } else if (appType === "docker-compose" && options.dockerCompose) {
369
+ body.docker_compose = options.dockerCompose;
330
370
  }
331
371
 
332
- log.debug(`Create application body: ${JSON.stringify(body, null, 2)}`)
333
- log.debug(`Endpoint: POST ${endpoint}`)
372
+ log.debug(`Create application body: ${JSON.stringify(body, null, 2)}`);
373
+ log.debug(`Endpoint: POST ${endpoint}`);
334
374
 
335
375
  const result = await this.request<{ uuid: string }>(endpoint, {
336
- method: 'POST',
376
+ method: "POST",
337
377
  body: JSON.stringify(body),
338
- })
378
+ });
339
379
 
340
380
  if (result.error) {
341
- log.error(`Failed to create application: ${result.error}`)
342
- return err(new Error(result.error))
381
+ log.error(`Failed to create application: ${result.error}`);
382
+ return err(new Error(result.error));
343
383
  }
344
384
 
345
- onProgress?.(100, `Application "${options.name}" created`)
385
+ onProgress?.(100, `Application "${options.name}" created`);
346
386
 
347
- log.success(`Application created: ${result.data?.uuid}`)
387
+ log.success(`Application created: ${result.data?.uuid}`);
348
388
  return ok({
349
389
  success: true,
350
390
  uuid: result.data?.uuid,
351
- })
391
+ });
352
392
  }
353
393
 
354
394
  /**
@@ -360,44 +400,50 @@ export class CoolifyService {
360
400
  */
361
401
  async setEnvironmentVariables(
362
402
  appUuid: string,
363
- envVars: Record<string, string>
403
+ envVars: Record<string, string>,
364
404
  ): Promise<Result<void, Error>> {
365
- log.info(`Setting ${Object.keys(envVars).length} environment variables for ${appUuid}`)
405
+ log.info(
406
+ `Setting ${Object.keys(envVars).length} environment variables for ${appUuid}`,
407
+ );
366
408
 
367
409
  // Coolify API: POST to create, PATCH to update existing
368
410
  for (const [key, value] of Object.entries(envVars)) {
369
411
  const result = await this.request(`/applications/${appUuid}/envs`, {
370
- method: 'POST',
412
+ method: "POST",
371
413
  body: JSON.stringify({ key, value, is_preview: false }),
372
- })
414
+ });
373
415
 
374
- if (result.error && result.error.includes('already exists')) {
416
+ if (result.error && result.error.includes("already exists")) {
375
417
  // Var exists — DELETE + re-POST (Coolify PATCH on envs returns 404)
376
- const listResult = await this.request<Array<{ uuid: string; key: string }>>(`/applications/${appUuid}/envs`)
377
- const existing = listResult.data?.find((v) => v.key === key)
418
+ const listResult = await this.request<
419
+ Array<{ uuid: string; key: string }>
420
+ >(`/applications/${appUuid}/envs`);
421
+ const existing = listResult.data?.find((v) => v.key === key);
378
422
  if (existing) {
379
- await this.request(`/applications/${appUuid}/envs/${existing.uuid}`, { method: 'DELETE' })
423
+ await this.request(`/applications/${appUuid}/envs/${existing.uuid}`, {
424
+ method: "DELETE",
425
+ });
380
426
  const repost = await this.request(`/applications/${appUuid}/envs`, {
381
- method: 'POST',
427
+ method: "POST",
382
428
  body: JSON.stringify({ key, value, is_preview: false }),
383
- })
429
+ });
384
430
  if (repost.error) {
385
- log.error(`Failed to update env var ${key}: ${repost.error}`)
386
- return err(new Error(`Failed to update ${key}: ${repost.error}`))
431
+ log.error(`Failed to update env var ${key}: ${repost.error}`);
432
+ return err(new Error(`Failed to update ${key}: ${repost.error}`));
387
433
  }
388
- log.debug(`Updated existing env var: ${key}`)
434
+ log.debug(`Updated existing env var: ${key}`);
389
435
  } else {
390
- log.error(`Failed to set env var ${key}: ${result.error}`)
391
- return err(new Error(`Failed to set ${key}: ${result.error}`))
436
+ log.error(`Failed to set env var ${key}: ${result.error}`);
437
+ return err(new Error(`Failed to set ${key}: ${result.error}`));
392
438
  }
393
439
  } else if (result.error) {
394
- log.error(`Failed to set env var ${key}: ${result.error}`)
395
- return err(new Error(`Failed to set ${key}: ${result.error}`))
440
+ log.error(`Failed to set env var ${key}: ${result.error}`);
441
+ return err(new Error(`Failed to set ${key}: ${result.error}`));
396
442
  }
397
443
  }
398
444
 
399
- log.success(`${Object.keys(envVars).length} environment variables set`)
400
- return ok(undefined)
445
+ log.success(`${Object.keys(envVars).length} environment variables set`);
446
+ return ok(undefined);
401
447
  }
402
448
 
403
449
  /**
@@ -407,21 +453,21 @@ export class CoolifyService {
407
453
  * @returns Result with environment variables or error
408
454
  */
409
455
  async getEnvironmentVariables(
410
- appUuid: string
456
+ appUuid: string,
411
457
  ): Promise<Result<ICoolifyEnvVar[], Error>> {
412
- log.info(`Getting environment variables for ${appUuid}`)
458
+ log.info(`Getting environment variables for ${appUuid}`);
413
459
 
414
460
  const result = await this.request<ICoolifyEnvVar[]>(
415
- `/applications/${appUuid}/envs`
416
- )
461
+ `/applications/${appUuid}/envs`,
462
+ );
417
463
 
418
464
  if (result.error) {
419
- log.error(`Failed to get env vars: ${result.error}`)
420
- return err(new Error(result.error))
465
+ log.error(`Failed to get env vars: ${result.error}`);
466
+ return err(new Error(result.error));
421
467
  }
422
468
 
423
- log.success(`Environment variables retrieved for ${appUuid}`)
424
- return ok(result.data || [])
469
+ log.success(`Environment variables retrieved for ${appUuid}`);
470
+ return ok(result.data || []);
425
471
  }
426
472
 
427
473
  /**
@@ -438,46 +484,52 @@ export class CoolifyService {
438
484
  appUuid: string,
439
485
  key: string,
440
486
  value: string,
441
- isBuildTime: boolean = false
487
+ _isBuildTime: boolean = false,
442
488
  ): Promise<Result<void, Error>> {
443
- log.info(`Setting environment variable ${key} for ${appUuid}`)
489
+ log.info(`Setting environment variable ${key} for ${appUuid}`);
444
490
 
445
491
  // Check if variable already exists
446
- const existingVars = await this.getEnvironmentVariables(appUuid)
492
+ const existingVars = await this.getEnvironmentVariables(appUuid);
447
493
  if (isErr(existingVars)) {
448
- return err(existingVars.error)
494
+ return err(existingVars.error);
449
495
  }
450
496
 
451
- const exists = existingVars.value.some(ev => ev.key === key)
497
+ const exists = existingVars.value.some((ev) => ev.key === key);
452
498
 
453
499
  if (exists) {
454
500
  // Use PATCH to update existing variable
455
- log.debug(`Variable ${key} exists, using PATCH to update`)
456
- const result = await this.request<{ uuid: string }>(`/applications/${appUuid}/envs`, {
457
- method: 'PATCH',
458
- body: JSON.stringify({ key, value }),
459
- })
501
+ log.debug(`Variable ${key} exists, using PATCH to update`);
502
+ const result = await this.request<{ uuid: string }>(
503
+ `/applications/${appUuid}/envs`,
504
+ {
505
+ method: "PATCH",
506
+ body: JSON.stringify({ key, value }),
507
+ },
508
+ );
460
509
 
461
510
  if (result.error) {
462
- log.error(`Failed to update env var: ${result.error}`)
463
- return err(new Error(result.error))
511
+ log.error(`Failed to update env var: ${result.error}`);
512
+ return err(new Error(result.error));
464
513
  }
465
514
  } else {
466
515
  // Use POST to create new variable
467
- log.debug(`Variable ${key} does not exist, using POST to create`)
468
- const result = await this.request<{ uuid: string }>(`/applications/${appUuid}/envs`, {
469
- method: 'POST',
470
- body: JSON.stringify({ key, value }),
471
- })
516
+ log.debug(`Variable ${key} does not exist, using POST to create`);
517
+ const result = await this.request<{ uuid: string }>(
518
+ `/applications/${appUuid}/envs`,
519
+ {
520
+ method: "POST",
521
+ body: JSON.stringify({ key, value }),
522
+ },
523
+ );
472
524
 
473
525
  if (result.error) {
474
- log.error(`Failed to create env var: ${result.error}`)
475
- return err(new Error(result.error))
526
+ log.error(`Failed to create env var: ${result.error}`);
527
+ return err(new Error(result.error));
476
528
  }
477
529
  }
478
530
 
479
- log.success(`Environment variable ${key} set for ${appUuid}`)
480
- return ok(undefined)
531
+ log.success(`Environment variable ${key} set for ${appUuid}`);
532
+ return ok(undefined);
481
533
  }
482
534
 
483
535
  /**
@@ -489,33 +541,36 @@ export class CoolifyService {
489
541
  */
490
542
  async deleteEnvironmentVariable(
491
543
  appUuid: string,
492
- key: string
544
+ key: string,
493
545
  ): Promise<Result<void, Error>> {
494
- log.info(`Deleting environment variable ${key} from ${appUuid}`)
546
+ log.info(`Deleting environment variable ${key} from ${appUuid}`);
495
547
 
496
548
  // First get all env vars to find the UUID of the one to delete
497
- const envVarsResult = await this.getEnvironmentVariables(appUuid)
549
+ const envVarsResult = await this.getEnvironmentVariables(appUuid);
498
550
  if (isErr(envVarsResult)) {
499
- return err(envVarsResult.error)
551
+ return err(envVarsResult.error);
500
552
  }
501
553
 
502
- const envVar = envVarsResult.value.find(ev => ev.key === key)
554
+ const envVar = envVarsResult.value.find((ev) => ev.key === key);
503
555
  if (!envVar) {
504
- log.error(`Environment variable ${key} not found`)
505
- return err(new Error(`Environment variable ${key} not found`))
556
+ log.error(`Environment variable ${key} not found`);
557
+ return err(new Error(`Environment variable ${key} not found`));
506
558
  }
507
559
 
508
- const result = await this.request(`/applications/${appUuid}/envs/${envVar.uuid}`, {
509
- method: 'DELETE',
510
- })
560
+ const result = await this.request(
561
+ `/applications/${appUuid}/envs/${envVar.uuid}`,
562
+ {
563
+ method: "DELETE",
564
+ },
565
+ );
511
566
 
512
567
  if (result.error) {
513
- log.error(`Failed to delete env var: ${result.error}`)
514
- return err(new Error(result.error))
568
+ log.error(`Failed to delete env var: ${result.error}`);
569
+ return err(new Error(result.error));
515
570
  }
516
571
 
517
- log.success(`Environment variable ${key} deleted from ${appUuid}`)
518
- return ok(undefined)
572
+ log.success(`Environment variable ${key} deleted from ${appUuid}`);
573
+ return ok(undefined);
519
574
  }
520
575
 
521
576
  /**
@@ -524,33 +579,44 @@ export class CoolifyService {
524
579
  * @param appUuid - Application UUID
525
580
  * @returns Result with status or error
526
581
  */
527
- async getApplicationStatus(
528
- appUuid: string
529
- ): Promise<Result<string, Error>> {
582
+ async getApplicationStatus(appUuid: string): Promise<Result<string, Error>> {
530
583
  const result = await this.request<{ status: string }>(
531
- `/applications/${appUuid}`
532
- )
584
+ `/applications/${appUuid}`,
585
+ );
533
586
 
534
587
  if (result.error) {
535
- return err(new Error(result.error))
588
+ return err(new Error(result.error));
536
589
  }
537
590
 
538
- return ok(result.data?.status || 'unknown')
591
+ return ok(result.data?.status || "unknown");
539
592
  }
540
593
 
541
594
  /**
542
595
  * Lists available servers in Coolify.
543
596
  *
597
+ * @param page - Optional page number for pagination
598
+ * @param perPage - Optional number of items per page
544
599
  * @returns Result with servers or error
545
600
  */
546
- async listServers(): Promise<Result<ICoolifyServer[], Error>> {
547
- const result = await this.request<ICoolifyServer[]>('/servers')
601
+ async listServers(
602
+ page?: number,
603
+ perPage?: number,
604
+ ): Promise<Result<ICoolifyServer[], Error>> {
605
+ let endpoint = "/servers";
606
+ const params = new URLSearchParams();
607
+ if (page) params.set("page", page.toString());
608
+ if (perPage) params.set("per_page", perPage.toString());
609
+ if (params.toString()) {
610
+ endpoint += `?${params.toString()}`;
611
+ }
612
+
613
+ const result = await this.request<ICoolifyServer[]>(endpoint);
548
614
 
549
615
  if (result.error) {
550
- return err(new Error(result.error))
616
+ return err(new Error(result.error));
551
617
  }
552
618
 
553
- return ok(result.data || [])
619
+ return ok(result.data || []);
554
620
  }
555
621
 
556
622
  /**
@@ -559,46 +625,78 @@ export class CoolifyService {
559
625
  * @param serverUuid - Server UUID
560
626
  * @returns Result with server details or error
561
627
  */
562
- async getServer(
563
- serverUuid: string
564
- ): Promise<Result<ICoolifyServer, Error>> {
565
- log.info(`Getting server details for ${serverUuid}`)
628
+ async getServer(serverUuid: string): Promise<Result<ICoolifyServer, Error>> {
629
+ log.info(`Getting server details for ${serverUuid}`);
566
630
 
567
- const result = await this.request<ICoolifyServer>(`/servers/${serverUuid}`)
631
+ const result = await this.request<ICoolifyServer>(`/servers/${serverUuid}`);
568
632
 
569
633
  if (result.error) {
570
- log.error(`Failed to get server: ${result.error}`)
571
- return err(new Error(result.error))
634
+ log.error(`Failed to get server: ${result.error}`);
635
+ return err(new Error(result.error));
572
636
  }
573
637
 
574
- log.success(`Server details retrieved: ${serverUuid}`)
575
- return ok(result.data as ICoolifyServer)
638
+ log.success(`Server details retrieved: ${serverUuid}`);
639
+ return ok(result.data as ICoolifyServer);
576
640
  }
577
641
 
578
642
  /**
579
643
  * Lists all GitHub Apps configured in Coolify.
580
644
  *
645
+ * @param page - Optional page number for pagination
646
+ * @param perPage - Optional number of items per page
581
647
  * @returns Result with GitHub Apps list or error
582
648
  */
583
- async listGithubApps(): Promise<Result<Array<{ id: number; uuid: string; name: string; is_public: boolean }>, Error>> {
584
- const result = await this.request<Array<{ id: number; uuid: string; name: string; is_public: boolean }>>('/github-apps')
585
- if (result.error) return err(new Error(result.error))
586
- return ok(result.data || [])
649
+ async listGithubApps(
650
+ page?: number,
651
+ perPage?: number,
652
+ ): Promise<
653
+ Result<
654
+ Array<{ id: number; uuid: string; name: string; is_public: boolean }>,
655
+ Error
656
+ >
657
+ > {
658
+ let endpoint = "/github-apps";
659
+ const params = new URLSearchParams();
660
+ if (page) params.set("page", page.toString());
661
+ if (perPage) params.set("per_page", perPage.toString());
662
+ if (params.toString()) {
663
+ endpoint += `?${params.toString()}`;
664
+ }
665
+
666
+ const result =
667
+ await this.request<
668
+ Array<{ id: number; uuid: string; name: string; is_public: boolean }>
669
+ >(endpoint);
670
+ if (result.error) return err(new Error(result.error));
671
+ return ok(result.data || []);
587
672
  }
588
673
 
589
674
  /**
590
675
  * Lists all projects.
591
676
  *
677
+ * @param page - Optional page number for pagination
678
+ * @param perPage - Optional number of items per page
592
679
  * @returns Result with projects list or error
593
680
  */
594
- async listProjects(): Promise<Result<ICoolifyProject[], Error>> {
595
- const result = await this.request<ICoolifyProject[]>('/projects')
681
+ async listProjects(
682
+ page?: number,
683
+ perPage?: number,
684
+ ): Promise<Result<ICoolifyProject[], Error>> {
685
+ let endpoint = "/projects";
686
+ const params = new URLSearchParams();
687
+ if (page) params.set("page", page.toString());
688
+ if (perPage) params.set("per_page", perPage.toString());
689
+ if (params.toString()) {
690
+ endpoint += `?${params.toString()}`;
691
+ }
692
+
693
+ const result = await this.request<ICoolifyProject[]>(endpoint);
596
694
 
597
695
  if (result.error) {
598
- return err(new Error(result.error))
696
+ return err(new Error(result.error));
599
697
  }
600
698
 
601
- return ok(result.data || [])
699
+ return ok(result.data || []);
602
700
  }
603
701
 
604
702
  /**
@@ -610,22 +708,22 @@ export class CoolifyService {
610
708
  */
611
709
  async createProject(
612
710
  name: string,
613
- description?: string
711
+ description?: string,
614
712
  ): Promise<Result<ICoolifyProject, Error>> {
615
- log.info(`Creating project: ${name}`)
713
+ log.info(`Creating project: ${name}`);
616
714
 
617
- const result = await this.request<ICoolifyProject>('/projects', {
618
- method: 'POST',
619
- body: JSON.stringify({ name, description: description || '' }),
620
- })
715
+ const result = await this.request<ICoolifyProject>("/projects", {
716
+ method: "POST",
717
+ body: JSON.stringify({ name, description: description || "" }),
718
+ });
621
719
 
622
720
  if (result.error) {
623
- log.error(`Failed to create project: ${result.error}`)
624
- return err(new Error(result.error))
721
+ log.error(`Failed to create project: ${result.error}`);
722
+ return err(new Error(result.error));
625
723
  }
626
724
 
627
- log.success(`Project created: ${result.data?.uuid}`)
628
- return ok(result.data!)
725
+ log.success(`Project created: ${result.data?.uuid}`);
726
+ return ok(result.data!);
629
727
  }
630
728
 
631
729
  /**
@@ -635,36 +733,49 @@ export class CoolifyService {
635
733
  * @returns Result with environments list or error
636
734
  */
637
735
  async getProjectEnvironments(
638
- projectUuid: string
736
+ projectUuid: string,
639
737
  ): Promise<Result<ICoolifyEnvironment[], Error>> {
640
- log.info(`Getting environments for project ${projectUuid}`)
738
+ log.info(`Getting environments for project ${projectUuid}`);
641
739
 
642
740
  const result = await this.request<{ environments: ICoolifyEnvironment[] }>(
643
- `/projects/${projectUuid}`
644
- )
741
+ `/projects/${projectUuid}`,
742
+ );
645
743
 
646
744
  if (result.error) {
647
- log.error(`Failed to get environments: ${result.error}`)
648
- return err(new Error(result.error))
745
+ log.error(`Failed to get environments: ${result.error}`);
746
+ return err(new Error(result.error));
649
747
  }
650
748
 
651
- log.success(`Environments retrieved for project ${projectUuid}`)
652
- return ok(result.data?.environments || [])
749
+ log.success(`Environments retrieved for project ${projectUuid}`);
750
+ return ok(result.data?.environments || []);
653
751
  }
654
752
 
655
753
  /**
656
754
  * Lists all teams.
657
755
  *
756
+ * @param page - Optional page number for pagination
757
+ * @param perPage - Optional number of items per page
658
758
  * @returns Result with teams list or error
659
759
  */
660
- async listTeams(): Promise<Result<ICoolifyTeam[], Error>> {
661
- const result = await this.request<ICoolifyTeam[]>('/teams')
760
+ async listTeams(
761
+ page?: number,
762
+ perPage?: number,
763
+ ): Promise<Result<ICoolifyTeam[], Error>> {
764
+ let endpoint = "/teams";
765
+ const params = new URLSearchParams();
766
+ if (page) params.set("page", page.toString());
767
+ if (perPage) params.set("per_page", perPage.toString());
768
+ if (params.toString()) {
769
+ endpoint += `?${params.toString()}`;
770
+ }
771
+
772
+ const result = await this.request<ICoolifyTeam[]>(endpoint);
662
773
 
663
774
  if (result.error) {
664
- return err(new Error(result.error))
775
+ return err(new Error(result.error));
665
776
  }
666
777
 
667
- return ok(result.data || [])
778
+ return ok(result.data || []);
668
779
  }
669
780
 
670
781
  /**
@@ -674,17 +785,17 @@ export class CoolifyService {
674
785
  * @returns Result with destinations or error
675
786
  */
676
787
  async getServerDestinations(
677
- serverUuid: string
788
+ serverUuid: string,
678
789
  ): Promise<Result<ICoolifyDestination[], Error>> {
679
790
  const result = await this.request<{
680
- destinations: ICoolifyDestination[]
681
- }>(`/servers/${serverUuid}`)
791
+ destinations: ICoolifyDestination[];
792
+ }>(`/servers/${serverUuid}`);
682
793
 
683
794
  if (result.error) {
684
- return err(new Error(result.error))
795
+ return err(new Error(result.error));
685
796
  }
686
797
 
687
- return ok(result.data?.destinations || [])
798
+ return ok(result.data?.destinations || []);
688
799
  }
689
800
 
690
801
  /**
@@ -692,55 +803,79 @@ export class CoolifyService {
692
803
  *
693
804
  * @param teamId - Optional team ID to filter by
694
805
  * @param projectId - Optional project ID to filter by
806
+ * @param page - Optional page number for pagination
807
+ * @param perPage - Optional number of items per page
695
808
  * @returns Result with applications list or error
696
809
  */
697
810
  async listApplications(
698
811
  teamId?: string,
699
- projectId?: string
812
+ projectId?: string,
813
+ page?: number,
814
+ perPage?: number,
700
815
  ): Promise<Result<ICoolifyApplication[], Error>> {
701
- log.info('Listing applications')
702
-
703
- let endpoint = '/applications'
704
- const params = new URLSearchParams()
705
- if (teamId) params.set('team_id', teamId)
706
- if (projectId) params.set('project_id', projectId)
816
+ log.info("Listing applications");
817
+
818
+ let endpoint = "/applications";
819
+ const params = new URLSearchParams();
820
+ if (teamId) params.set("team_id", teamId);
821
+ if (projectId) params.set("project_id", projectId);
822
+ if (page) params.set("page", page.toString());
823
+ if (perPage) params.set("per_page", perPage.toString());
707
824
  if (params.toString()) {
708
- endpoint += `?${params.toString()}`
825
+ endpoint += `?${params.toString()}`;
709
826
  }
710
827
 
711
- const result = await this.request<ICoolifyApplication[]>(endpoint)
828
+ const result = await this.request<ICoolifyApplication[]>(endpoint);
712
829
 
713
830
  if (result.error) {
714
- log.error(`Failed to list applications: ${result.error}`)
715
- return err(new Error(result.error))
831
+ log.error(`Failed to list applications: ${result.error}`);
832
+ return err(new Error(result.error));
716
833
  }
717
834
 
718
- log.success(`Listed ${result.data?.length || 0} applications`)
719
- return ok(result.data || [])
835
+ log.success(`Listed ${result.data?.length || 0} applications`);
836
+ return ok(result.data || []);
720
837
  }
721
838
 
722
839
  /**
723
840
  * Deletes an application.
724
841
  *
725
842
  * @param appUuid - Application UUID
843
+ * @param options - Delete options for cascade deletion
726
844
  * @returns Result indicating success or error
727
845
  */
728
846
  async deleteApplication(
729
- appUuid: string
847
+ appUuid: string,
848
+ options?: {
849
+ deleteConfigurations?: boolean;
850
+ deleteVolumes?: boolean;
851
+ dockerCleanup?: boolean;
852
+ deleteConnectedNetworks?: boolean;
853
+ },
730
854
  ): Promise<Result<ICoolifyDeleteResult, Error>> {
731
- log.info(`Deleting application ${appUuid}`)
855
+ log.info(`Deleting application ${appUuid}`);
732
856
 
733
- const result = await this.request<ICoolifyDeleteResult>(`/applications/${appUuid}`, {
734
- method: 'DELETE',
735
- })
857
+ const params = new URLSearchParams();
858
+ if (options?.deleteConfigurations)
859
+ params.set("delete_configurations", "true");
860
+ if (options?.deleteVolumes) params.set("delete_volumes", "true");
861
+ if (options?.dockerCleanup) params.set("docker_cleanup", "true");
862
+ if (options?.deleteConnectedNetworks)
863
+ params.set("delete_connected_networks", "true");
864
+
865
+ const queryString = params.toString();
866
+ const endpoint = `/applications/${appUuid}${queryString ? `?${queryString}` : ""}`;
867
+
868
+ const result = await this.request<ICoolifyDeleteResult>(endpoint, {
869
+ method: "DELETE",
870
+ });
736
871
 
737
872
  if (result.error) {
738
- log.error(`Failed to delete application: ${result.error}`)
739
- return err(new Error(result.error))
873
+ log.error(`Failed to delete application: ${result.error}`);
874
+ return err(new Error(result.error));
740
875
  }
741
876
 
742
- log.success(`Application deleted: ${appUuid}`)
743
- return ok({ success: true, message: 'Application deleted' })
877
+ log.success(`Application deleted: ${appUuid}`);
878
+ return ok({ success: true, message: "Application deleted" });
744
879
  }
745
880
 
746
881
  /**
@@ -752,38 +887,45 @@ export class CoolifyService {
752
887
  */
753
888
  async updateApplication(
754
889
  appUuid: string,
755
- options: ICoolifyUpdateOptions
890
+ options: ICoolifyUpdateOptions,
756
891
  ): Promise<Result<ICoolifyApplication, Error>> {
757
- log.info(`Updating application ${appUuid}`)
758
-
759
- const body: Record<string, unknown> = {}
760
- if (options.name) body.name = options.name
761
- if (options.description) body.description = options.description
762
- if (options.buildPack) body.build_pack = options.buildPack
763
- if (options.gitBranch) body.git_branch = options.gitBranch
764
- if (options.portsExposes) body.ports_exposes = options.portsExposes
765
- if (options.installCommand) body.install_command = options.installCommand
766
- if (options.buildCommand) body.build_command = options.buildCommand
767
- if (options.startCommand) body.start_command = options.startCommand
768
- if (options.dockerfileLocation) body.dockerfile_location = options.dockerfileLocation
769
- if (options.baseDirectory) body.base_directory = options.baseDirectory
770
- if (options.domains) body.domains = options.domains
771
- if (options.dockerComposeDomains) body.docker_compose_domains = options.dockerComposeDomains
772
- if (options.isForceHttpsEnabled !== undefined) body.is_force_https_enabled = options.isForceHttpsEnabled
773
- if (options.isAutoDeployEnabled !== undefined) body.is_auto_deploy_enabled = options.isAutoDeployEnabled
774
-
775
- const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}`, {
776
- method: 'PATCH',
777
- body: JSON.stringify(body),
778
- })
892
+ log.info(`Updating application ${appUuid}`);
893
+
894
+ const body: Record<string, unknown> = {};
895
+ if (options.name) body.name = options.name;
896
+ if (options.description) body.description = options.description;
897
+ if (options.buildPack) body.build_pack = options.buildPack;
898
+ if (options.gitBranch) body.git_branch = options.gitBranch;
899
+ if (options.portsExposes) body.ports_exposes = options.portsExposes;
900
+ if (options.installCommand) body.install_command = options.installCommand;
901
+ if (options.buildCommand) body.build_command = options.buildCommand;
902
+ if (options.startCommand) body.start_command = options.startCommand;
903
+ if (options.dockerfileLocation)
904
+ body.dockerfile_location = options.dockerfileLocation;
905
+ if (options.baseDirectory) body.base_directory = options.baseDirectory;
906
+ if (options.domains) body.domains = options.domains;
907
+ if (options.dockerComposeDomains)
908
+ body.docker_compose_domains = options.dockerComposeDomains;
909
+ if (options.isForceHttpsEnabled !== undefined)
910
+ body.is_force_https_enabled = options.isForceHttpsEnabled;
911
+ if (options.isAutoDeployEnabled !== undefined)
912
+ body.is_auto_deploy_enabled = options.isAutoDeployEnabled;
913
+
914
+ const result = await this.request<ICoolifyApplication>(
915
+ `/applications/${appUuid}`,
916
+ {
917
+ method: "PATCH",
918
+ body: JSON.stringify(body),
919
+ },
920
+ );
779
921
 
780
922
  if (result.error) {
781
- log.error(`Failed to update application: ${result.error}`)
782
- return err(new Error(result.error))
923
+ log.error(`Failed to update application: ${result.error}`);
924
+ return err(new Error(result.error));
783
925
  }
784
926
 
785
- log.success(`Application updated: ${appUuid}`)
786
- return ok(result.data as ICoolifyApplication)
927
+ log.success(`Application updated: ${appUuid}`);
928
+ return ok(result.data as ICoolifyApplication);
787
929
  }
788
930
 
789
931
  /**
@@ -795,37 +937,102 @@ export class CoolifyService {
795
937
  */
796
938
  async getApplicationLogs(
797
939
  appUuid: string,
798
- options: ICoolifyLogsOptions = {}
940
+ options: ICoolifyLogsOptions = {},
799
941
  ): Promise<Result<ICoolifyLogs, Error>> {
800
- log.info(`Getting logs for application ${appUuid}`)
942
+ log.info(`Getting logs for application ${appUuid}`);
801
943
 
802
- const params = new URLSearchParams()
803
- if (options.follow) params.set('follow', 'true')
804
- if (options.tail) params.set('tail', options.tail.toString())
944
+ const params = new URLSearchParams();
945
+ if (options.follow) params.set("follow", "true");
946
+ if (options.tail) params.set("lines", options.tail.toString());
947
+ if (options.serviceName) params.set("service_name", options.serviceName);
805
948
 
806
- const endpoint = `/applications/${appUuid}/logs${params.toString() ? `?${params.toString()}` : ''}`
949
+ const endpoint = `/applications/${appUuid}/logs${params.toString() ? `?${params.toString()}` : ""}`;
807
950
 
808
- const result = await this.request<{ logs: string | string[] }>(endpoint)
951
+ const result = await this.request<{ logs: string | string[] }>(endpoint);
809
952
 
810
953
  if (result.error) {
811
- log.error(`Failed to get logs: ${result.error}`)
812
- return err(new Error(result.error))
954
+ log.error(`Failed to get logs: ${result.error}`);
955
+ return err(new Error(result.error));
813
956
  }
814
957
 
815
958
  // Coolify API returns logs as a single newline-delimited string for
816
959
  // docker-compose apps, but as string[] for single-container apps.
817
- const rawLogs = result.data?.logs
960
+ const rawLogs = result.data?.logs;
818
961
  const logsArray: string[] = Array.isArray(rawLogs)
819
962
  ? rawLogs
820
- : typeof rawLogs === 'string'
821
- ? rawLogs.split('\n').filter((l: string) => l.length > 0)
822
- : []
963
+ : typeof rawLogs === "string"
964
+ ? rawLogs.split("\n").filter((l: string) => l.length > 0)
965
+ : [];
823
966
 
824
- log.success(`Logs retrieved for application: ${appUuid}`)
967
+ log.success(`Logs retrieved for application: ${appUuid}`);
825
968
  return ok({
826
969
  logs: logsArray,
827
970
  timestamp: new Date().toISOString(),
828
- })
971
+ });
972
+ }
973
+
974
+ /**
975
+ * Executes a command on an application's running container.
976
+ *
977
+ * @param appUuid - Application UUID
978
+ * @param command - Shell command to execute
979
+ * @returns Result with command output or error
980
+ */
981
+ async executeCommand(
982
+ appUuid: string,
983
+ command: string,
984
+ ): Promise<Result<{ message?: string; response?: string }, Error>> {
985
+ log.info(`Executing command on application ${appUuid}`);
986
+
987
+ const result = await this.request<{
988
+ message?: string;
989
+ response?: string;
990
+ }>(`/applications/${appUuid}/execute-command`, {
991
+ method: "POST",
992
+ body: JSON.stringify({ command }),
993
+ });
994
+
995
+ if (result.error) {
996
+ log.error(`Failed to execute command: ${result.error}`);
997
+ return err(new Error(result.error));
998
+ }
999
+
1000
+ log.success(`Command executed on ${appUuid}`);
1001
+ return ok(result.data || { message: "Command executed" });
1002
+ }
1003
+
1004
+ /**
1005
+ * Bulk updates environment variables for an application.
1006
+ *
1007
+ * @param appUuid - Application UUID
1008
+ * @param envVars - Array of { key, value, is_preview? } objects
1009
+ * @returns Result indicating success or error
1010
+ */
1011
+ async bulkUpdateEnvironmentVariables(
1012
+ appUuid: string,
1013
+ envVars: Array<{
1014
+ key: string;
1015
+ value: string;
1016
+ is_preview?: boolean;
1017
+ }>,
1018
+ ): Promise<Result<{ message: string }, Error>> {
1019
+ log.info(`Bulk updating ${envVars.length} env vars for ${appUuid}`);
1020
+
1021
+ const result = await this.request<{ message: string }>(
1022
+ `/applications/${appUuid}/envs/bulk`,
1023
+ {
1024
+ method: "PATCH",
1025
+ body: JSON.stringify(envVars),
1026
+ },
1027
+ );
1028
+
1029
+ if (result.error) {
1030
+ log.error(`Failed to bulk update env vars: ${result.error}`);
1031
+ return err(new Error(result.error));
1032
+ }
1033
+
1034
+ log.success(`Bulk updated ${envVars.length} env vars for ${appUuid}`);
1035
+ return ok(result.data || { message: "Environment variables updated" });
829
1036
  }
830
1037
 
831
1038
  /**
@@ -835,111 +1042,1131 @@ export class CoolifyService {
835
1042
  * @returns Result with deployment history or error
836
1043
  */
837
1044
  async getApplicationDeploymentHistory(
838
- appUuid: string
1045
+ appUuid: string,
839
1046
  ): Promise<Result<ICoolifyDeployment[], Error>> {
840
- log.info(`Getting deployment history for ${appUuid}`)
1047
+ log.info(`Getting deployment history for ${appUuid}`);
841
1048
 
842
- // According to Coolify API docs, endpoint is /applications/{app_uuid}/deployments
843
- const result = await this.request<{ deployments: ICoolifyDeployment[] }>(`/applications/${appUuid}/deployments`)
1049
+ // Coolify API: /deployments/applications/{appUuid}
1050
+ // Response: { count: number, deployments: ICoolifyDeployment[] }
1051
+ const result = await this.request<{
1052
+ count: number;
1053
+ deployments: ICoolifyDeployment[];
1054
+ }>(`/deployments/applications/${appUuid}`);
844
1055
 
845
1056
  if (result.error) {
846
- log.error(`Failed to get deployment history: ${result.error}`)
847
- return err(new Error(result.error))
1057
+ log.error(`Failed to get deployment history: ${result.error}`);
1058
+ return err(new Error(result.error));
848
1059
  }
849
1060
 
850
- log.success(`Deployment history retrieved for ${appUuid}`)
851
- return ok(result.data?.deployments || [])
1061
+ log.success(`Deployment history retrieved for ${appUuid}`);
1062
+ return ok(result.data?.deployments || []);
852
1063
  }
853
1064
 
854
1065
  /**
855
1066
  * Starts a stopped application.
1067
+ * Note: Coolify API uses GET for application start/stop/restart.
856
1068
  *
857
1069
  * @param appUuid - Application UUID
1070
+ * @param options - Optional start options (force, instant_deploy)
858
1071
  * @returns Result with application status or error
859
1072
  */
860
1073
  async startApplication(
861
- appUuid: string
1074
+ appUuid: string,
1075
+ options?: { force?: boolean; instantDeploy?: boolean },
862
1076
  ): Promise<Result<ICoolifyApplication, Error>> {
863
- log.info(`Starting application ${appUuid}`)
1077
+ log.info(`Starting application ${appUuid}`);
1078
+
1079
+ const params = new URLSearchParams();
1080
+ if (options?.force) params.set("force", "true");
1081
+ if (options?.instantDeploy) params.set("instant_deploy", "true");
1082
+ const queryString = params.toString();
1083
+ const endpoint = `/applications/${appUuid}/start${queryString ? `?${queryString}` : ""}`;
864
1084
 
865
- const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}/start`, {
866
- method: 'POST',
867
- })
1085
+ const result = await this.request<ICoolifyApplication>(endpoint, {
1086
+ method: "GET",
1087
+ });
868
1088
 
869
1089
  if (result.error) {
870
- log.error(`Failed to start application: ${result.error}`)
871
- return err(new Error(result.error))
1090
+ log.error(`Failed to start application: ${result.error}`);
1091
+ return err(new Error(result.error));
872
1092
  }
873
1093
 
874
- log.success(`Application started: ${appUuid}`)
875
- return ok(result.data as ICoolifyApplication)
1094
+ log.success(`Application started: ${appUuid}`);
1095
+ return ok(result.data as ICoolifyApplication);
876
1096
  }
877
1097
 
878
1098
  /**
879
1099
  * Stops a running application.
1100
+ * Note: Coolify API uses GET for application start/stop/restart.
880
1101
  *
881
1102
  * @param appUuid - Application UUID
882
1103
  * @returns Result with application status or error
883
1104
  */
884
1105
  async stopApplication(
885
- appUuid: string
1106
+ appUuid: string,
886
1107
  ): Promise<Result<ICoolifyApplication, Error>> {
887
- log.info(`Stopping application ${appUuid}`)
1108
+ log.info(`Stopping application ${appUuid}`);
888
1109
 
889
- const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}/stop`, {
890
- method: 'POST',
891
- })
1110
+ const result = await this.request<ICoolifyApplication>(
1111
+ `/applications/${appUuid}/stop`,
1112
+ { method: "GET" },
1113
+ );
892
1114
 
893
1115
  if (result.error) {
894
- log.error(`Failed to stop application: ${result.error}`)
895
- return err(new Error(result.error))
1116
+ log.error(`Failed to stop application: ${result.error}`);
1117
+ return err(new Error(result.error));
896
1118
  }
897
1119
 
898
- log.success(`Application stopped: ${appUuid}`)
899
- return ok(result.data as ICoolifyApplication)
1120
+ log.success(`Application stopped: ${appUuid}`);
1121
+ return ok(result.data as ICoolifyApplication);
900
1122
  }
901
1123
 
902
1124
  /**
903
1125
  * Restarts an application.
1126
+ * Note: Coolify API uses GET for application start/stop/restart.
904
1127
  *
905
1128
  * @param appUuid - Application UUID
906
1129
  * @returns Result with application status or error
907
1130
  */
908
1131
  async restartApplication(
909
- appUuid: string
1132
+ appUuid: string,
910
1133
  ): Promise<Result<ICoolifyApplication, Error>> {
911
- log.info(`Restarting application ${appUuid}`)
1134
+ log.info(`Restarting application ${appUuid}`);
912
1135
 
913
- const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}/restart`, {
914
- method: 'POST',
915
- })
1136
+ const result = await this.request<ICoolifyApplication>(
1137
+ `/applications/${appUuid}/restart`,
1138
+ { method: "GET" },
1139
+ );
916
1140
 
917
1141
  if (result.error) {
918
- log.error(`Failed to restart application: ${result.error}`)
919
- return err(new Error(result.error))
1142
+ log.error(`Failed to restart application: ${result.error}`);
1143
+ return err(new Error(result.error));
920
1144
  }
921
1145
 
922
- log.success(`Application restarted: ${appUuid}`)
923
- return ok(result.data as ICoolifyApplication)
1146
+ log.success(`Application restarted: ${appUuid}`);
1147
+ return ok(result.data as ICoolifyApplication);
1148
+ }
1149
+
1150
+ // ===========================================================================
1151
+ // Version / Health
1152
+ // ===========================================================================
1153
+
1154
+ /**
1155
+ * Gets the Coolify server version.
1156
+ *
1157
+ * @returns Result with version info or error
1158
+ */
1159
+ async getVersion(): Promise<Result<ICoolifyVersion, Error>> {
1160
+ log.info("Getting Coolify version");
1161
+
1162
+ // /version returns plain text, not JSON
1163
+ if (!this.baseUrl || !this.token) {
1164
+ return err(new Error("Coolify not configured"));
1165
+ }
1166
+
1167
+ try {
1168
+ const baseUrl = this.baseUrl.replace(/\/+$/, "");
1169
+ const url = `${baseUrl}/api/v1/version`;
1170
+ const response = await fetch(url, {
1171
+ headers: {
1172
+ Authorization: `Bearer ${this.token}`,
1173
+ },
1174
+ });
1175
+
1176
+ if (!response.ok) {
1177
+ return err(
1178
+ new Error(`HTTP ${response.status}: ${response.statusText}`),
1179
+ );
1180
+ }
1181
+
1182
+ const version = (await response.text()).trim();
1183
+ log.success(`Coolify version: ${version}`);
1184
+ return ok({ version });
1185
+ } catch (error) {
1186
+ const message = error instanceof Error ? error.message : "Unknown error";
1187
+ return err(new Error(message));
1188
+ }
1189
+ }
1190
+
1191
+ // ===========================================================================
1192
+ // Database endpoints
1193
+ // ===========================================================================
1194
+
1195
+ /**
1196
+ * Lists all databases.
1197
+ *
1198
+ * @param page - Optional page number
1199
+ * @param perPage - Optional items per page
1200
+ * @returns Result with databases list or error
1201
+ */
1202
+ async listDatabases(
1203
+ page?: number,
1204
+ perPage?: number,
1205
+ ): Promise<Result<ICoolifyDatabase[], Error>> {
1206
+ log.info("Listing databases");
1207
+
1208
+ let endpoint = "/databases";
1209
+ const params = new URLSearchParams();
1210
+ if (page) params.set("page", page.toString());
1211
+ if (perPage) params.set("per_page", perPage.toString());
1212
+ if (params.toString()) endpoint += `?${params.toString()}`;
1213
+
1214
+ const result = await this.request<ICoolifyDatabase[]>(endpoint);
1215
+ if (result.error) return err(new Error(result.error));
1216
+
1217
+ log.success(`Listed ${result.data?.length || 0} databases`);
1218
+ return ok(result.data || []);
1219
+ }
1220
+
1221
+ /**
1222
+ * Gets details of a specific database.
1223
+ *
1224
+ * @param uuid - Database UUID
1225
+ * @returns Result with database details or error
1226
+ */
1227
+ async getDatabase(uuid: string): Promise<Result<ICoolifyDatabase, Error>> {
1228
+ log.info(`Getting database ${uuid}`);
1229
+
1230
+ const result = await this.request<ICoolifyDatabase>(`/databases/${uuid}`);
1231
+ if (result.error) return err(new Error(result.error));
1232
+
1233
+ return ok(result.data as ICoolifyDatabase);
1234
+ }
1235
+
1236
+ /**
1237
+ * Creates a database of the specified type.
1238
+ *
1239
+ * @param dbType - Database type (postgresql, mysql, mariadb, mongodb, redis, keydb, clickhouse, dragonfly)
1240
+ * @param data - Database creation data
1241
+ * @returns Result with created database UUID or error
1242
+ */
1243
+ async createDatabase(
1244
+ dbType: string,
1245
+ data: Record<string, unknown>,
1246
+ ): Promise<Result<{ uuid: string }, Error>> {
1247
+ log.info(`Creating ${dbType} database`);
1248
+
1249
+ const result = await this.request<{ uuid: string }>(
1250
+ `/databases/${dbType}`,
1251
+ {
1252
+ method: "POST",
1253
+ body: JSON.stringify(data),
1254
+ },
1255
+ );
1256
+ if (result.error) return err(new Error(result.error));
1257
+
1258
+ log.success(`Database created: ${result.data?.uuid}`);
1259
+ return ok(result.data!);
1260
+ }
1261
+
1262
+ /**
1263
+ * Updates a database configuration.
1264
+ *
1265
+ * @param uuid - Database UUID
1266
+ * @param data - Update data
1267
+ * @returns Result with updated database or error
1268
+ */
1269
+ async updateDatabase(
1270
+ uuid: string,
1271
+ data: Record<string, unknown>,
1272
+ ): Promise<Result<ICoolifyDatabase, Error>> {
1273
+ log.info(`Updating database ${uuid}`);
1274
+
1275
+ const result = await this.request<ICoolifyDatabase>(`/databases/${uuid}`, {
1276
+ method: "PATCH",
1277
+ body: JSON.stringify(data),
1278
+ });
1279
+ if (result.error) return err(new Error(result.error));
1280
+
1281
+ log.success(`Database updated: ${uuid}`);
1282
+ return ok(result.data as ICoolifyDatabase);
1283
+ }
1284
+
1285
+ /**
1286
+ * Deletes a database.
1287
+ *
1288
+ * @param uuid - Database UUID
1289
+ * @param options - Delete options for cascade deletion
1290
+ * @returns Result indicating success or error
1291
+ */
1292
+ async deleteDatabase(
1293
+ uuid: string,
1294
+ options?: {
1295
+ deleteConfigurations?: boolean;
1296
+ deleteVolumes?: boolean;
1297
+ dockerCleanup?: boolean;
1298
+ deleteConnectedNetworks?: boolean;
1299
+ },
1300
+ ): Promise<Result<ICoolifyDeleteResult, Error>> {
1301
+ log.info(`Deleting database ${uuid}`);
1302
+
1303
+ const params = new URLSearchParams();
1304
+ if (options?.deleteConfigurations)
1305
+ params.set("delete_configurations", "true");
1306
+ if (options?.deleteVolumes) params.set("delete_volumes", "true");
1307
+ if (options?.dockerCleanup) params.set("docker_cleanup", "true");
1308
+ if (options?.deleteConnectedNetworks)
1309
+ params.set("delete_connected_networks", "true");
1310
+
1311
+ const queryString = params.toString();
1312
+ const endpoint = `/databases/${uuid}${queryString ? `?${queryString}` : ""}`;
1313
+
1314
+ const result = await this.request<ICoolifyDeleteResult>(endpoint, {
1315
+ method: "DELETE",
1316
+ });
1317
+ if (result.error) return err(new Error(result.error));
1318
+
1319
+ log.success(`Database deleted: ${uuid}`);
1320
+ return ok({ success: true, message: "Database deleted" });
1321
+ }
1322
+
1323
+ /**
1324
+ * Starts a database.
1325
+ *
1326
+ * @param uuid - Database UUID
1327
+ * @returns Result indicating success or error
1328
+ */
1329
+ async startDatabase(
1330
+ uuid: string,
1331
+ ): Promise<Result<{ message: string }, Error>> {
1332
+ log.info(`Starting database ${uuid}`);
1333
+
1334
+ const result = await this.request<{ message: string }>(
1335
+ `/databases/${uuid}/start`,
1336
+ { method: "GET" },
1337
+ );
1338
+ if (result.error) return err(new Error(result.error));
1339
+
1340
+ log.success(`Database started: ${uuid}`);
1341
+ return ok(result.data || { message: "Database started" });
1342
+ }
1343
+
1344
+ /**
1345
+ * Stops a database.
1346
+ *
1347
+ * @param uuid - Database UUID
1348
+ * @returns Result indicating success or error
1349
+ */
1350
+ async stopDatabase(
1351
+ uuid: string,
1352
+ ): Promise<Result<{ message: string }, Error>> {
1353
+ log.info(`Stopping database ${uuid}`);
1354
+
1355
+ const result = await this.request<{ message: string }>(
1356
+ `/databases/${uuid}/stop`,
1357
+ { method: "GET" },
1358
+ );
1359
+ if (result.error) return err(new Error(result.error));
1360
+
1361
+ log.success(`Database stopped: ${uuid}`);
1362
+ return ok(result.data || { message: "Database stopped" });
1363
+ }
1364
+
1365
+ /**
1366
+ * Restarts a database.
1367
+ *
1368
+ * @param uuid - Database UUID
1369
+ * @returns Result indicating success or error
1370
+ */
1371
+ async restartDatabase(
1372
+ uuid: string,
1373
+ ): Promise<Result<{ message: string }, Error>> {
1374
+ log.info(`Restarting database ${uuid}`);
1375
+
1376
+ const result = await this.request<{ message: string }>(
1377
+ `/databases/${uuid}/restart`,
1378
+ { method: "GET" },
1379
+ );
1380
+ if (result.error) return err(new Error(result.error));
1381
+
1382
+ log.success(`Database restarted: ${uuid}`);
1383
+ return ok(result.data || { message: "Database restarted" });
1384
+ }
1385
+
1386
+ // ===========================================================================
1387
+ // Database Backup endpoints
1388
+ // ===========================================================================
1389
+
1390
+ /**
1391
+ * Lists backups for a database.
1392
+ *
1393
+ * @param databaseUuid - Database UUID
1394
+ * @returns Result with backups list or error
1395
+ */
1396
+ async listDatabaseBackups(
1397
+ databaseUuid: string,
1398
+ ): Promise<Result<ICoolifyDatabaseBackup[], Error>> {
1399
+ log.info(`Listing backups for database ${databaseUuid}`);
1400
+
1401
+ const result = await this.request<ICoolifyDatabaseBackup[]>(
1402
+ `/databases/${databaseUuid}/backups`,
1403
+ );
1404
+ if (result.error) return err(new Error(result.error));
1405
+
1406
+ return ok(result.data || []);
1407
+ }
1408
+
1409
+ /**
1410
+ * Gets a specific database backup.
1411
+ *
1412
+ * @param databaseUuid - Database UUID
1413
+ * @param backupUuid - Backup UUID
1414
+ * @returns Result with backup details or error
1415
+ */
1416
+ async getDatabaseBackup(
1417
+ databaseUuid: string,
1418
+ backupUuid: string,
1419
+ ): Promise<Result<ICoolifyDatabaseBackup, Error>> {
1420
+ const result = await this.request<ICoolifyDatabaseBackup>(
1421
+ `/databases/${databaseUuid}/backups/${backupUuid}`,
1422
+ );
1423
+ if (result.error) return err(new Error(result.error));
1424
+
1425
+ return ok(result.data as ICoolifyDatabaseBackup);
1426
+ }
1427
+
1428
+ /**
1429
+ * Creates a database backup.
1430
+ *
1431
+ * @param databaseUuid - Database UUID
1432
+ * @param data - Backup creation data
1433
+ * @returns Result with created backup or error
1434
+ */
1435
+ async createDatabaseBackup(
1436
+ databaseUuid: string,
1437
+ data: Record<string, unknown>,
1438
+ ): Promise<Result<ICoolifyDatabaseBackup, Error>> {
1439
+ log.info(`Creating backup for database ${databaseUuid}`);
1440
+
1441
+ const result = await this.request<ICoolifyDatabaseBackup>(
1442
+ `/databases/${databaseUuid}/backups`,
1443
+ { method: "POST", body: JSON.stringify(data) },
1444
+ );
1445
+ if (result.error) return err(new Error(result.error));
1446
+
1447
+ log.success("Database backup created");
1448
+ return ok(result.data as ICoolifyDatabaseBackup);
1449
+ }
1450
+
1451
+ /**
1452
+ * Updates a database backup.
1453
+ *
1454
+ * @param databaseUuid - Database UUID
1455
+ * @param backupUuid - Backup UUID
1456
+ * @param data - Update data
1457
+ * @returns Result indicating success or error
1458
+ */
1459
+ async updateDatabaseBackup(
1460
+ databaseUuid: string,
1461
+ backupUuid: string,
1462
+ data: Record<string, unknown>,
1463
+ ): Promise<Result<{ message: string }, Error>> {
1464
+ const result = await this.request<{ message: string }>(
1465
+ `/databases/${databaseUuid}/backups/${backupUuid}`,
1466
+ { method: "PATCH", body: JSON.stringify(data) },
1467
+ );
1468
+ if (result.error) return err(new Error(result.error));
1469
+
1470
+ return ok(result.data || { message: "Backup updated" });
1471
+ }
1472
+
1473
+ /**
1474
+ * Deletes a database backup.
1475
+ *
1476
+ * @param databaseUuid - Database UUID
1477
+ * @param backupUuid - Backup UUID
1478
+ * @returns Result indicating success or error
1479
+ */
1480
+ async deleteDatabaseBackup(
1481
+ databaseUuid: string,
1482
+ backupUuid: string,
1483
+ ): Promise<Result<{ message: string }, Error>> {
1484
+ const result = await this.request<{ message: string }>(
1485
+ `/databases/${databaseUuid}/backups/${backupUuid}`,
1486
+ { method: "DELETE" },
1487
+ );
1488
+ if (result.error) return err(new Error(result.error));
1489
+
1490
+ return ok(result.data || { message: "Backup deleted" });
1491
+ }
1492
+
1493
+ // ===========================================================================
1494
+ // Service endpoints
1495
+ // ===========================================================================
1496
+
1497
+ /**
1498
+ * Lists all services.
1499
+ *
1500
+ * @param page - Optional page number
1501
+ * @param perPage - Optional items per page
1502
+ * @returns Result with services list or error
1503
+ */
1504
+ async listServices(
1505
+ page?: number,
1506
+ perPage?: number,
1507
+ ): Promise<Result<ICoolifyServiceType[], Error>> {
1508
+ log.info("Listing services");
1509
+
1510
+ let endpoint = "/services";
1511
+ const params = new URLSearchParams();
1512
+ if (page) params.set("page", page.toString());
1513
+ if (perPage) params.set("per_page", perPage.toString());
1514
+ if (params.toString()) endpoint += `?${params.toString()}`;
1515
+
1516
+ const result = await this.request<ICoolifyServiceType[]>(endpoint);
1517
+ if (result.error) return err(new Error(result.error));
1518
+
1519
+ log.success(`Listed ${result.data?.length || 0} services`);
1520
+ return ok(result.data || []);
1521
+ }
1522
+
1523
+ /**
1524
+ * Gets details of a specific service.
1525
+ *
1526
+ * @param uuid - Service UUID
1527
+ * @returns Result with service details or error
1528
+ */
1529
+ async getService(uuid: string): Promise<Result<ICoolifyServiceType, Error>> {
1530
+ log.info(`Getting service ${uuid}`);
1531
+
1532
+ const result = await this.request<ICoolifyServiceType>(`/services/${uuid}`);
1533
+ if (result.error) return err(new Error(result.error));
1534
+
1535
+ return ok(result.data as ICoolifyServiceType);
1536
+ }
1537
+
1538
+ /**
1539
+ * Creates a new service.
1540
+ *
1541
+ * @param data - Service creation data
1542
+ * @returns Result with created service or error
1543
+ */
1544
+ async createService(
1545
+ data: Record<string, unknown>,
1546
+ ): Promise<Result<{ uuid: string }, Error>> {
1547
+ log.info("Creating service");
1548
+
1549
+ const result = await this.request<{ uuid: string }>("/services", {
1550
+ method: "POST",
1551
+ body: JSON.stringify(data),
1552
+ });
1553
+ if (result.error) return err(new Error(result.error));
1554
+
1555
+ log.success(`Service created: ${result.data?.uuid}`);
1556
+ return ok(result.data!);
1557
+ }
1558
+
1559
+ /**
1560
+ * Updates a service configuration.
1561
+ *
1562
+ * @param uuid - Service UUID
1563
+ * @param data - Update data
1564
+ * @returns Result with updated service or error
1565
+ */
1566
+ async updateService(
1567
+ uuid: string,
1568
+ data: Record<string, unknown>,
1569
+ ): Promise<Result<ICoolifyServiceType, Error>> {
1570
+ log.info(`Updating service ${uuid}`);
1571
+
1572
+ const result = await this.request<ICoolifyServiceType>(
1573
+ `/services/${uuid}`,
1574
+ {
1575
+ method: "PATCH",
1576
+ body: JSON.stringify(data),
1577
+ },
1578
+ );
1579
+ if (result.error) return err(new Error(result.error));
1580
+
1581
+ log.success(`Service updated: ${uuid}`);
1582
+ return ok(result.data as ICoolifyServiceType);
1583
+ }
1584
+
1585
+ /**
1586
+ * Deletes a service.
1587
+ *
1588
+ * @param uuid - Service UUID
1589
+ * @param options - Delete options for cascade deletion
1590
+ * @returns Result indicating success or error
1591
+ */
1592
+ async deleteService(
1593
+ uuid: string,
1594
+ options?: {
1595
+ deleteConfigurations?: boolean;
1596
+ deleteVolumes?: boolean;
1597
+ dockerCleanup?: boolean;
1598
+ deleteConnectedNetworks?: boolean;
1599
+ },
1600
+ ): Promise<Result<ICoolifyDeleteResult, Error>> {
1601
+ log.info(`Deleting service ${uuid}`);
1602
+
1603
+ const params = new URLSearchParams();
1604
+ if (options?.deleteConfigurations)
1605
+ params.set("delete_configurations", "true");
1606
+ if (options?.deleteVolumes) params.set("delete_volumes", "true");
1607
+ if (options?.dockerCleanup) params.set("docker_cleanup", "true");
1608
+ if (options?.deleteConnectedNetworks)
1609
+ params.set("delete_connected_networks", "true");
1610
+
1611
+ const queryString = params.toString();
1612
+ const endpoint = `/services/${uuid}${queryString ? `?${queryString}` : ""}`;
1613
+
1614
+ const result = await this.request<ICoolifyDeleteResult>(endpoint, {
1615
+ method: "DELETE",
1616
+ });
1617
+ if (result.error) return err(new Error(result.error));
1618
+
1619
+ log.success(`Service deleted: ${uuid}`);
1620
+ return ok({ success: true, message: "Service deleted" });
1621
+ }
1622
+
1623
+ /**
1624
+ * Starts a service.
1625
+ * Note: Coolify API uses GET for service start/stop/restart.
1626
+ *
1627
+ * @param uuid - Service UUID
1628
+ * @returns Result indicating success or error
1629
+ */
1630
+ async startService(
1631
+ uuid: string,
1632
+ ): Promise<Result<{ message: string }, Error>> {
1633
+ log.info(`Starting service ${uuid}`);
1634
+
1635
+ const result = await this.request<{ message: string }>(
1636
+ `/services/${uuid}/start`,
1637
+ { method: "GET" },
1638
+ );
1639
+ if (result.error) return err(new Error(result.error));
1640
+
1641
+ log.success(`Service started: ${uuid}`);
1642
+ return ok(result.data || { message: "Service started" });
1643
+ }
1644
+
1645
+ /**
1646
+ * Stops a service.
1647
+ * Note: Coolify API uses GET for service start/stop/restart.
1648
+ *
1649
+ * @param uuid - Service UUID
1650
+ * @returns Result indicating success or error
1651
+ */
1652
+ async stopService(uuid: string): Promise<Result<{ message: string }, Error>> {
1653
+ log.info(`Stopping service ${uuid}`);
1654
+
1655
+ const result = await this.request<{ message: string }>(
1656
+ `/services/${uuid}/stop`,
1657
+ { method: "GET" },
1658
+ );
1659
+ if (result.error) return err(new Error(result.error));
1660
+
1661
+ log.success(`Service stopped: ${uuid}`);
1662
+ return ok(result.data || { message: "Service stopped" });
1663
+ }
1664
+
1665
+ /**
1666
+ * Restarts a service.
1667
+ * Note: Coolify API uses GET for service start/stop/restart.
1668
+ *
1669
+ * @param uuid - Service UUID
1670
+ * @returns Result indicating success or error
1671
+ */
1672
+ async restartService(
1673
+ uuid: string,
1674
+ ): Promise<Result<{ message: string }, Error>> {
1675
+ log.info(`Restarting service ${uuid}`);
1676
+
1677
+ const result = await this.request<{ message: string }>(
1678
+ `/services/${uuid}/restart`,
1679
+ { method: "GET" },
1680
+ );
1681
+ if (result.error) return err(new Error(result.error));
1682
+
1683
+ log.success(`Service restarted: ${uuid}`);
1684
+ return ok(result.data || { message: "Service restarted" });
1685
+ }
1686
+
1687
+ /**
1688
+ * Lists environment variables for a service.
1689
+ *
1690
+ * @param uuid - Service UUID
1691
+ * @returns Result with env vars list or error
1692
+ */
1693
+ async listServiceEnvVars(
1694
+ uuid: string,
1695
+ ): Promise<Result<ICoolifyEnvVar[], Error>> {
1696
+ const result = await this.request<ICoolifyEnvVar[]>(
1697
+ `/services/${uuid}/envs`,
1698
+ );
1699
+ if (result.error) return err(new Error(result.error));
1700
+
1701
+ return ok(result.data || []);
1702
+ }
1703
+
1704
+ /**
1705
+ * Creates an environment variable for a service.
1706
+ *
1707
+ * @param uuid - Service UUID
1708
+ * @param data - Env var data (key, value, is_preview)
1709
+ * @returns Result with created env var UUID or error
1710
+ */
1711
+ async createServiceEnvVar(
1712
+ uuid: string,
1713
+ data: { key: string; value: string; is_preview?: boolean },
1714
+ ): Promise<Result<{ uuid: string }, Error>> {
1715
+ const result = await this.request<{ uuid: string }>(
1716
+ `/services/${uuid}/envs`,
1717
+ { method: "POST", body: JSON.stringify(data) },
1718
+ );
1719
+ if (result.error) return err(new Error(result.error));
1720
+
1721
+ return ok(result.data!);
1722
+ }
1723
+
1724
+ // ===========================================================================
1725
+ // Additional Server endpoints
1726
+ // ===========================================================================
1727
+
1728
+ /**
1729
+ * Gets resources deployed on a server.
1730
+ *
1731
+ * @param serverUuid - Server UUID
1732
+ * @returns Result with server resources or error
1733
+ */
1734
+ async getServerResources(
1735
+ serverUuid: string,
1736
+ ): Promise<Result<ICoolifyServerResource[], Error>> {
1737
+ log.info(`Getting resources for server ${serverUuid}`);
1738
+
1739
+ const result = await this.request<ICoolifyServerResource[]>(
1740
+ `/servers/${serverUuid}/resources`,
1741
+ );
1742
+ if (result.error) return err(new Error(result.error));
1743
+
1744
+ return ok(result.data || []);
1745
+ }
1746
+
1747
+ /**
1748
+ * Gets domains configured on a server.
1749
+ *
1750
+ * @param serverUuid - Server UUID
1751
+ * @returns Result with server domains or error
1752
+ */
1753
+ async getServerDomains(
1754
+ serverUuid: string,
1755
+ ): Promise<Result<ICoolifyServerDomain[], Error>> {
1756
+ log.info(`Getting domains for server ${serverUuid}`);
1757
+
1758
+ const result = await this.request<ICoolifyServerDomain[]>(
1759
+ `/servers/${serverUuid}/domains`,
1760
+ );
1761
+ if (result.error) return err(new Error(result.error));
1762
+
1763
+ return ok(result.data || []);
1764
+ }
1765
+
1766
+ /**
1767
+ * Validates a server connection.
1768
+ *
1769
+ * @param serverUuid - Server UUID
1770
+ * @returns Result with validation status or error
1771
+ */
1772
+ async validateServer(
1773
+ serverUuid: string,
1774
+ ): Promise<Result<{ message: string }, Error>> {
1775
+ log.info(`Validating server ${serverUuid}`);
1776
+
1777
+ const result = await this.request<{ message: string }>(
1778
+ `/servers/${serverUuid}/validate`,
1779
+ );
1780
+ if (result.error) return err(new Error(result.error));
1781
+
1782
+ return ok(result.data || { message: "Server validated" });
1783
+ }
1784
+
1785
+ /**
1786
+ * Creates a new server.
1787
+ *
1788
+ * @param data - Server creation data
1789
+ * @returns Result with created server UUID or error
1790
+ */
1791
+ async createServer(
1792
+ data: Record<string, unknown>,
1793
+ ): Promise<Result<{ uuid: string }, Error>> {
1794
+ log.info("Creating server");
1795
+
1796
+ const result = await this.request<{ uuid: string }>("/servers", {
1797
+ method: "POST",
1798
+ body: JSON.stringify(data),
1799
+ });
1800
+ if (result.error) return err(new Error(result.error));
1801
+
1802
+ log.success(`Server created: ${result.data?.uuid}`);
1803
+ return ok(result.data!);
1804
+ }
1805
+
1806
+ /**
1807
+ * Deletes a server.
1808
+ *
1809
+ * @param serverUuid - Server UUID
1810
+ * @returns Result indicating success or error
1811
+ */
1812
+ async deleteServer(
1813
+ serverUuid: string,
1814
+ ): Promise<Result<{ message: string }, Error>> {
1815
+ log.info(`Deleting server ${serverUuid}`);
1816
+
1817
+ const result = await this.request<{ message: string }>(
1818
+ `/servers/${serverUuid}`,
1819
+ { method: "DELETE" },
1820
+ );
1821
+ if (result.error) return err(new Error(result.error));
1822
+
1823
+ log.success(`Server deleted: ${serverUuid}`);
1824
+ return ok(result.data || { message: "Server deleted" });
1825
+ }
1826
+
1827
+ // ===========================================================================
1828
+ // Additional Project endpoints
1829
+ // ===========================================================================
1830
+
1831
+ /**
1832
+ * Updates a project.
1833
+ *
1834
+ * @param uuid - Project UUID
1835
+ * @param data - Update data (name, description)
1836
+ * @returns Result with updated project or error
1837
+ */
1838
+ async updateProject(
1839
+ uuid: string,
1840
+ data: { name?: string; description?: string },
1841
+ ): Promise<Result<ICoolifyProject, Error>> {
1842
+ log.info(`Updating project ${uuid}`);
1843
+
1844
+ const result = await this.request<ICoolifyProject>(`/projects/${uuid}`, {
1845
+ method: "PATCH",
1846
+ body: JSON.stringify(data),
1847
+ });
1848
+ if (result.error) return err(new Error(result.error));
1849
+
1850
+ log.success(`Project updated: ${uuid}`);
1851
+ return ok(result.data as ICoolifyProject);
1852
+ }
1853
+
1854
+ /**
1855
+ * Deletes a project.
1856
+ *
1857
+ * @param uuid - Project UUID
1858
+ * @returns Result indicating success or error
1859
+ */
1860
+ async deleteProject(
1861
+ uuid: string,
1862
+ ): Promise<Result<{ message: string }, Error>> {
1863
+ log.info(`Deleting project ${uuid}`);
1864
+
1865
+ const result = await this.request<{ message: string }>(
1866
+ `/projects/${uuid}`,
1867
+ { method: "DELETE" },
1868
+ );
1869
+ if (result.error) return err(new Error(result.error));
1870
+
1871
+ log.success(`Project deleted: ${uuid}`);
1872
+ return ok(result.data || { message: "Project deleted" });
1873
+ }
1874
+
1875
+ /**
1876
+ * Creates a new environment within a project.
1877
+ *
1878
+ * @param projectUuid - Project UUID
1879
+ * @param data - Environment creation data (name, description)
1880
+ * @returns Result with created environment UUID or error
1881
+ */
1882
+ async createProjectEnvironment(
1883
+ projectUuid: string,
1884
+ data: { name: string; description?: string },
1885
+ ): Promise<Result<{ uuid: string }, Error>> {
1886
+ log.info(`Creating environment in project ${projectUuid}`);
1887
+
1888
+ const result = await this.request<{ uuid: string }>(
1889
+ `/projects/${projectUuid}/environments`,
1890
+ { method: "POST", body: JSON.stringify(data) },
1891
+ );
1892
+ if (result.error) return err(new Error(result.error));
1893
+
1894
+ log.success("Environment created");
1895
+ return ok(result.data!);
1896
+ }
1897
+
1898
+ // ===========================================================================
1899
+ // Additional Team endpoints
1900
+ // ===========================================================================
1901
+
1902
+ /**
1903
+ * Gets the current team.
1904
+ *
1905
+ * @returns Result with current team or error
1906
+ */
1907
+ async getCurrentTeam(): Promise<Result<ICoolifyTeam, Error>> {
1908
+ log.info("Getting current team");
1909
+
1910
+ const result = await this.request<ICoolifyTeam>("/teams/current");
1911
+ if (result.error) return err(new Error(result.error));
1912
+
1913
+ return ok(result.data as ICoolifyTeam);
1914
+ }
1915
+
1916
+ /**
1917
+ * Gets a specific team by ID.
1918
+ *
1919
+ * @param id - Team ID
1920
+ * @returns Result with team details or error
1921
+ */
1922
+ async getTeam(id: number): Promise<Result<ICoolifyTeam, Error>> {
1923
+ log.info(`Getting team ${id}`);
1924
+
1925
+ const result = await this.request<ICoolifyTeam>(`/teams/${id}`);
1926
+ if (result.error) return err(new Error(result.error));
1927
+
1928
+ return ok(result.data as ICoolifyTeam);
1929
+ }
1930
+
1931
+ /**
1932
+ * Gets members of a specific team.
1933
+ *
1934
+ * @param id - Team ID
1935
+ * @returns Result with team members or error
1936
+ */
1937
+ async getTeamMembers(
1938
+ id: number,
1939
+ ): Promise<
1940
+ Result<Array<{ id: number; name: string; email: string }>, Error>
1941
+ > {
1942
+ log.info(`Getting members for team ${id}`);
1943
+
1944
+ const result = await this.request<
1945
+ Array<{ id: number; name: string; email: string }>
1946
+ >(`/teams/${id}/members`);
1947
+ if (result.error) return err(new Error(result.error));
1948
+
1949
+ return ok(result.data || []);
1950
+ }
1951
+
1952
+ // ===========================================================================
1953
+ // Deployment Control
1954
+ // ===========================================================================
1955
+
1956
+ /**
1957
+ * Cancels a deployment.
1958
+ *
1959
+ * @param deploymentUuid - Deployment UUID
1960
+ * @returns Result indicating success or error
1961
+ */
1962
+ async cancelDeployment(
1963
+ deploymentUuid: string,
1964
+ ): Promise<Result<{ message: string }, Error>> {
1965
+ log.info(`Cancelling deployment ${deploymentUuid}`);
1966
+
1967
+ const result = await this.request<{ message: string }>(
1968
+ `/deployments/${deploymentUuid}/cancel`,
1969
+ { method: "POST" },
1970
+ );
1971
+ if (result.error) return err(new Error(result.error));
1972
+
1973
+ log.success(`Deployment cancelled: ${deploymentUuid}`);
1974
+ return ok(result.data || { message: "Deployment cancelled" });
1975
+ }
1976
+
1977
+ // ===========================================================================
1978
+ // SSH / Private Key endpoints
1979
+ // ===========================================================================
1980
+
1981
+ /**
1982
+ * Lists all private keys.
1983
+ *
1984
+ * @returns Result with private keys list or error
1985
+ */
1986
+ async listPrivateKeys(): Promise<Result<ICoolifyPrivateKey[], Error>> {
1987
+ log.info("Listing private keys");
1988
+
1989
+ const result = await this.request<ICoolifyPrivateKey[]>("/security/keys");
1990
+ if (result.error) return err(new Error(result.error));
1991
+
1992
+ return ok(result.data || []);
1993
+ }
1994
+
1995
+ /**
1996
+ * Gets a specific private key.
1997
+ *
1998
+ * @param uuid - Private key UUID
1999
+ * @returns Result with private key details or error
2000
+ */
2001
+ async getPrivateKey(
2002
+ uuid: string,
2003
+ ): Promise<Result<ICoolifyPrivateKey, Error>> {
2004
+ const result = await this.request<ICoolifyPrivateKey>(
2005
+ `/security/keys/${uuid}`,
2006
+ );
2007
+ if (result.error) return err(new Error(result.error));
2008
+
2009
+ return ok(result.data as ICoolifyPrivateKey);
2010
+ }
2011
+
2012
+ /**
2013
+ * Creates a new private key.
2014
+ *
2015
+ * @param data - Key creation data (name, private_key, description)
2016
+ * @returns Result with created key UUID or error
2017
+ */
2018
+ async createPrivateKey(data: {
2019
+ name: string;
2020
+ private_key: string;
2021
+ description?: string;
2022
+ }): Promise<Result<{ uuid: string }, Error>> {
2023
+ log.info("Creating private key");
2024
+
2025
+ const result = await this.request<{ uuid: string }>("/security/keys", {
2026
+ method: "POST",
2027
+ body: JSON.stringify(data),
2028
+ });
2029
+ if (result.error) return err(new Error(result.error));
2030
+
2031
+ log.success(`Private key created: ${result.data?.uuid}`);
2032
+ return ok(result.data!);
2033
+ }
2034
+
2035
+ /**
2036
+ * Updates a private key.
2037
+ *
2038
+ * @param uuid - Private key UUID
2039
+ * @param data - Update data
2040
+ * @returns Result with updated key or error
2041
+ */
2042
+ async updatePrivateKey(
2043
+ uuid: string,
2044
+ data: { name?: string; private_key?: string; description?: string },
2045
+ ): Promise<Result<ICoolifyPrivateKey, Error>> {
2046
+ const result = await this.request<ICoolifyPrivateKey>(
2047
+ `/security/keys/${uuid}`,
2048
+ { method: "PATCH", body: JSON.stringify(data) },
2049
+ );
2050
+ if (result.error) return err(new Error(result.error));
2051
+
2052
+ return ok(result.data as ICoolifyPrivateKey);
2053
+ }
2054
+
2055
+ /**
2056
+ * Deletes a private key.
2057
+ *
2058
+ * @param uuid - Private key UUID
2059
+ * @returns Result indicating success or error
2060
+ */
2061
+ async deletePrivateKey(
2062
+ uuid: string,
2063
+ ): Promise<Result<{ message: string }, Error>> {
2064
+ log.info(`Deleting private key ${uuid}`);
2065
+
2066
+ const result = await this.request<{ message: string }>(
2067
+ `/security/keys/${uuid}`,
2068
+ { method: "DELETE" },
2069
+ );
2070
+ if (result.error) return err(new Error(result.error));
2071
+
2072
+ log.success(`Private key deleted: ${uuid}`);
2073
+ return ok(result.data || { message: "Private key deleted" });
2074
+ }
2075
+
2076
+ // ===========================================================================
2077
+ // GitHub App endpoints (additional)
2078
+ // ===========================================================================
2079
+
2080
+ /**
2081
+ * Creates a GitHub App configuration.
2082
+ *
2083
+ * @param data - GitHub App creation data
2084
+ * @returns Result with created app or error
2085
+ */
2086
+ async createGitHubApp(
2087
+ data: Record<string, unknown>,
2088
+ ): Promise<Result<{ id: number; uuid: string }, Error>> {
2089
+ log.info("Creating GitHub App");
2090
+
2091
+ const result = await this.request<{ id: number; uuid: string }>(
2092
+ "/github-apps",
2093
+ { method: "POST", body: JSON.stringify(data) },
2094
+ );
2095
+ if (result.error) return err(new Error(result.error));
2096
+
2097
+ return ok(result.data!);
2098
+ }
2099
+
2100
+ /**
2101
+ * Updates a GitHub App configuration.
2102
+ *
2103
+ * @param id - GitHub App ID
2104
+ * @param data - Update data
2105
+ * @returns Result with updated app or error
2106
+ */
2107
+ async updateGitHubApp(
2108
+ id: number,
2109
+ data: Record<string, unknown>,
2110
+ ): Promise<Result<{ message: string }, Error>> {
2111
+ const result = await this.request<{ message: string }>(
2112
+ `/github-apps/${id}`,
2113
+ { method: "PATCH", body: JSON.stringify(data) },
2114
+ );
2115
+ if (result.error) return err(new Error(result.error));
2116
+
2117
+ return ok(result.data || { message: "GitHub App updated" });
2118
+ }
2119
+
2120
+ /**
2121
+ * Deletes a GitHub App configuration.
2122
+ *
2123
+ * @param id - GitHub App ID
2124
+ * @returns Result indicating success or error
2125
+ */
2126
+ async deleteGitHubApp(
2127
+ id: number,
2128
+ ): Promise<Result<{ message: string }, Error>> {
2129
+ log.info(`Deleting GitHub App ${id}`);
2130
+
2131
+ const result = await this.request<{ message: string }>(
2132
+ `/github-apps/${id}`,
2133
+ { method: "DELETE" },
2134
+ );
2135
+ if (result.error) return err(new Error(result.error));
2136
+
2137
+ return ok(result.data || { message: "GitHub App deleted" });
924
2138
  }
925
2139
 
926
2140
  /**
927
2141
  * Lists all active and queued deployments.
928
2142
  *
2143
+ * @param page - Optional page number for pagination
2144
+ * @param perPage - Optional number of items per page
929
2145
  * @returns Result with deployments list or error
930
2146
  */
931
- async listDeployments(): Promise<Result<ICoolifyDeployment[], Error>> {
932
- log.info('Listing active deployments')
2147
+ async listDeployments(
2148
+ page?: number,
2149
+ perPage?: number,
2150
+ ): Promise<Result<ICoolifyDeployment[], Error>> {
2151
+ log.info("Listing active deployments");
2152
+
2153
+ let endpoint = "/deployments";
2154
+ const params = new URLSearchParams();
2155
+ if (page) params.set("page", page.toString());
2156
+ if (perPage) params.set("per_page", perPage.toString());
2157
+ if (params.toString()) {
2158
+ endpoint += `?${params.toString()}`;
2159
+ }
933
2160
 
934
- const result = await this.request<ICoolifyDeployment[]>('/deployments')
2161
+ const result = await this.request<ICoolifyDeployment[]>(endpoint);
935
2162
 
936
2163
  if (result.error) {
937
- log.error(`Failed to list deployments: ${result.error}`)
938
- return err(new Error(result.error))
2164
+ log.error(`Failed to list deployments: ${result.error}`);
2165
+ return err(new Error(result.error));
939
2166
  }
940
2167
 
941
- log.success(`Listed ${result.data?.length || 0} active deployments`)
942
- return ok(result.data || [])
2168
+ log.success(`Listed ${result.data?.length || 0} active deployments`);
2169
+ return ok(result.data || []);
943
2170
  }
944
2171
 
945
2172
  /**
@@ -949,19 +2176,53 @@ export class CoolifyService {
949
2176
  * @returns Result with deployment details or error
950
2177
  */
951
2178
  async getDeployment(
952
- deploymentUuid: string
2179
+ deploymentUuid: string,
953
2180
  ): Promise<Result<ICoolifyDeployment, Error>> {
954
- log.info(`Getting deployment details for ${deploymentUuid}`)
2181
+ log.info(`Getting deployment details for ${deploymentUuid}`);
955
2182
 
956
- const result = await this.request<ICoolifyDeployment>(`/deployments/${deploymentUuid}`)
2183
+ const result = await this.request<ICoolifyDeployment>(
2184
+ `/deployments/${deploymentUuid}`,
2185
+ );
957
2186
 
958
2187
  if (result.error) {
959
- log.error(`Failed to get deployment: ${result.error}`)
960
- return err(new Error(result.error))
2188
+ log.error(`Failed to get deployment: ${result.error}`);
2189
+ return err(new Error(result.error));
961
2190
  }
962
2191
 
963
- log.success(`Deployment details retrieved: ${deploymentUuid}`)
964
- return ok(result.data as ICoolifyDeployment)
2192
+ log.success(`Deployment details retrieved: ${deploymentUuid}`);
2193
+ return ok(result.data as ICoolifyDeployment);
2194
+ }
2195
+
2196
+ /**
2197
+ * Gets logs for a specific deployment.
2198
+ *
2199
+ * @param deploymentUuid - Deployment UUID
2200
+ * @returns Result with deployment status and logs or error
2201
+ */
2202
+ async getDeploymentLogs(
2203
+ deploymentUuid: string,
2204
+ ): Promise<
2205
+ Result<{ status: string; logs: string; deployment_uuid: string }, Error>
2206
+ > {
2207
+ log.info(`Getting deployment logs for ${deploymentUuid}`);
2208
+
2209
+ const result = await this.request<{
2210
+ status: string;
2211
+ logs: string;
2212
+ deployment_uuid: string;
2213
+ }>(`/deployments/${deploymentUuid}`);
2214
+
2215
+ if (result.error) {
2216
+ log.error(`Failed to get deployment logs: ${result.error}`);
2217
+ return err(new Error(result.error));
2218
+ }
2219
+
2220
+ log.success(`Deployment logs retrieved: ${deploymentUuid}`);
2221
+ return ok({
2222
+ status: result.data?.status || "unknown",
2223
+ logs: result.data?.logs || "",
2224
+ deployment_uuid: result.data?.deployment_uuid || deploymentUuid,
2225
+ });
965
2226
  }
966
2227
 
967
2228
  /**
@@ -975,29 +2236,611 @@ export class CoolifyService {
975
2236
  async getApplicationDeployments(
976
2237
  appUuid: string,
977
2238
  skip: number = 0,
978
- take: number = 10
2239
+ take: number = 10,
979
2240
  ): Promise<Result<ICoolifyDeployment[], Error>> {
980
- log.info(`Getting deployments for application ${appUuid}`)
2241
+ log.info(`Getting deployments for application ${appUuid}`);
2242
+
2243
+ const params = new URLSearchParams();
2244
+ if (skip > 0) params.set("skip", skip.toString());
2245
+ if (take !== 10) params.set("take", take.toString());
2246
+
2247
+ const endpoint = `/applications/${appUuid}/deployments${params.toString() ? `?${params.toString()}` : ""}`;
2248
+ const result = await this.request<{
2249
+ count: number;
2250
+ deployments: ICoolifyDeployment[];
2251
+ }>(endpoint);
2252
+
2253
+ if (result.error) {
2254
+ log.error(`Failed to get application deployments: ${result.error}`);
2255
+ return err(new Error(result.error));
2256
+ }
2257
+
2258
+ const deployments = result.data?.deployments || [];
2259
+ log.success(`Retrieved ${deployments.length} deployments for ${appUuid}`);
2260
+ return ok(deployments);
2261
+ }
981
2262
 
982
- const params = new URLSearchParams()
983
- if (skip > 0) params.set('skip', skip.toString())
984
- if (take !== 10) params.set('take', take.toString())
2263
+ /**
2264
+ * Lists deployments for a specific application.
2265
+ *
2266
+ * Uses the /deployments/applications/{appUuid} endpoint which returns
2267
+ * all deployments (active, queued, and completed) for a single application.
2268
+ * This differs from listDeployments() which returns ALL deployments globally.
2269
+ *
2270
+ * @param appUuid - Application UUID
2271
+ * @returns Result with deployments list or error
2272
+ */
2273
+ async listApplicationDeployments(
2274
+ appUuid: string,
2275
+ ): Promise<Result<ICoolifyDeployment[], Error>> {
2276
+ log.info(`Listing deployments for application ${appUuid}`);
985
2277
 
986
- const endpoint = `/applications/${appUuid}/deployments${params.toString() ? `?${params.toString()}` : ''}`
987
- const result = await this.request<{ count: number; deployments: ICoolifyDeployment[] }>(endpoint)
2278
+ // API returns { count: number, deployments: ICoolifyDeployment[] }
2279
+ const result = await this.request<{
2280
+ count: number;
2281
+ deployments: ICoolifyDeployment[];
2282
+ }>(`/deployments/applications/${appUuid}`);
988
2283
 
989
2284
  if (result.error) {
990
- log.error(`Failed to get application deployments: ${result.error}`)
991
- return err(new Error(result.error))
2285
+ log.error(`Failed to list application deployments: ${result.error}`);
2286
+ return err(new Error(result.error));
2287
+ }
2288
+
2289
+ const deployments = result.data?.deployments || [];
2290
+ log.success(`Listed ${deployments.length} deployments for ${appUuid}`);
2291
+ return ok(deployments);
2292
+ }
2293
+
2294
+ // ===========================================================================
2295
+ // Smart Resolution Helpers
2296
+ // ===========================================================================
2297
+
2298
+ /**
2299
+ * Resolves an application by UUID, name, or domain (FQDN).
2300
+ *
2301
+ * @param query - UUID, name, or domain to search for
2302
+ * @returns Result with application or error
2303
+ */
2304
+ async resolveApplication(
2305
+ query: string,
2306
+ ): Promise<Result<ICoolifyApplication, Error>> {
2307
+ log.info(`Resolving application: ${query}`);
2308
+
2309
+ // If it looks like a UUID, try direct lookup first
2310
+ if (this.isLikelyUuid(query)) {
2311
+ const apps = await this.listApplications();
2312
+ if (isErr(apps)) return err(apps.error);
2313
+ const match = apps.value.find((a) => a.uuid === query);
2314
+ if (match) return ok(match);
2315
+ }
2316
+
2317
+ // Search by name or domain
2318
+ const apps = await this.listApplications();
2319
+ if (isErr(apps)) return err(apps.error);
2320
+
2321
+ const lowerQuery = query.toLowerCase();
2322
+ const matches = apps.value.filter(
2323
+ (a) =>
2324
+ a.name?.toLowerCase() === lowerQuery ||
2325
+ a.fqdn?.toLowerCase().includes(lowerQuery) ||
2326
+ a.uuid.startsWith(query),
2327
+ );
2328
+
2329
+ if (matches.length === 1) return ok(matches[0]);
2330
+ if (matches.length === 0) {
2331
+ return err(
2332
+ new Error(
2333
+ `No application found matching "${query}". Use 'list' to see available applications.`,
2334
+ ),
2335
+ );
2336
+ }
2337
+
2338
+ const names = matches.map((a) => ` - ${a.name} (${a.uuid})`).join("\n");
2339
+ return err(
2340
+ new Error(
2341
+ `Multiple applications match "${query}":\n${names}\nPlease use the full UUID.`,
2342
+ ),
2343
+ );
2344
+ }
2345
+
2346
+ /**
2347
+ * Resolves a server by UUID, name, or IP address.
2348
+ *
2349
+ * @param query - UUID, name, or IP to search for
2350
+ * @returns Result with server or error
2351
+ */
2352
+ async resolveServer(query: string): Promise<Result<ICoolifyServer, Error>> {
2353
+ log.info(`Resolving server: ${query}`);
2354
+
2355
+ const servers = await this.listServers();
2356
+ if (isErr(servers)) return err(servers.error);
2357
+
2358
+ const lowerQuery = query.toLowerCase();
2359
+ const match = servers.value.find(
2360
+ (s) =>
2361
+ s.uuid === query ||
2362
+ s.name?.toLowerCase() === lowerQuery ||
2363
+ s.ip === query ||
2364
+ s.uuid.startsWith(query),
2365
+ );
2366
+
2367
+ if (match) return ok(match);
2368
+ return err(
2369
+ new Error(
2370
+ `No server found matching "${query}". Use 'servers' to see available servers.`,
2371
+ ),
2372
+ );
2373
+ }
2374
+
2375
+ /**
2376
+ * Checks if a string looks like a UUID (Coolify or standard format).
2377
+ *
2378
+ * @param query - String to check
2379
+ * @returns true if it looks like a UUID
2380
+ */
2381
+ private isLikelyUuid(query: string): boolean {
2382
+ if (/^[a-z0-9]{20,}$/i.test(query)) return true;
2383
+ if (
2384
+ /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
2385
+ query,
2386
+ )
2387
+ )
2388
+ return true;
2389
+ return false;
2390
+ }
2391
+
2392
+ // ===========================================================================
2393
+ // Diagnostics
2394
+ // ===========================================================================
2395
+
2396
+ /**
2397
+ * Diagnoses an application by aggregating health, logs, deployments, and env vars.
2398
+ *
2399
+ * @param query - Application UUID, name, or domain
2400
+ * @returns Result with diagnostic report or error
2401
+ */
2402
+ async diagnoseApplication(query: string): Promise<
2403
+ Result<
2404
+ {
2405
+ application: ICoolifyApplication;
2406
+ recentDeployments: ICoolifyDeployment[];
2407
+ envVarCount: number;
2408
+ recentLogs: string[];
2409
+ issues: string[];
2410
+ },
2411
+ Error
2412
+ >
2413
+ > {
2414
+ log.info(`Diagnosing application: ${query}`);
2415
+
2416
+ const appResult = await this.resolveApplication(query);
2417
+ if (isErr(appResult)) return err(appResult.error);
2418
+
2419
+ const app = appResult.value;
2420
+ const issues: string[] = [];
2421
+
2422
+ // Gather data in parallel
2423
+ const [deploymentsResult, envResult, logsResult] = await Promise.all([
2424
+ this.getApplicationDeploymentHistory(app.uuid),
2425
+ this.getEnvironmentVariables(app.uuid),
2426
+ this.getApplicationLogs(app.uuid, { tail: 20 }),
2427
+ ]);
2428
+
2429
+ const deployments = isErr(deploymentsResult) ? [] : deploymentsResult.value;
2430
+ const envVarCount = isErr(envResult) ? 0 : envResult.value.length;
2431
+ const recentLogs = isErr(logsResult) ? [] : logsResult.value.logs;
2432
+
2433
+ // Check for issues
2434
+ if (app.status?.includes("stopped")) {
2435
+ issues.push("Application is stopped");
2436
+ }
2437
+ if (!app.fqdn) {
2438
+ issues.push("No domain configured");
2439
+ }
2440
+ if (envVarCount === 0) {
2441
+ issues.push("No environment variables set");
2442
+ }
2443
+
2444
+ const recentDeploys = deployments.slice(-5);
2445
+ const failedDeploys = recentDeploys.filter((d) =>
2446
+ d.status?.includes("failed"),
2447
+ );
2448
+ if (failedDeploys.length > 0) {
2449
+ issues.push(
2450
+ `${failedDeploys.length} of last ${recentDeploys.length} deployments failed`,
2451
+ );
2452
+ }
2453
+
2454
+ log.success(`Diagnosis complete for ${app.name}`);
2455
+ return ok({
2456
+ application: app,
2457
+ recentDeployments: recentDeploys,
2458
+ envVarCount,
2459
+ recentLogs,
2460
+ issues,
2461
+ });
2462
+ }
2463
+
2464
+ /**
2465
+ * Diagnoses a server by aggregating health, resources, and domains.
2466
+ *
2467
+ * @param query - Server UUID, name, or IP
2468
+ * @returns Result with diagnostic report or error
2469
+ */
2470
+ async diagnoseServer(query: string): Promise<
2471
+ Result<
2472
+ {
2473
+ server: ICoolifyServer;
2474
+ resources: ICoolifyServerResource[];
2475
+ domains: ICoolifyServerDomain[];
2476
+ issues: string[];
2477
+ },
2478
+ Error
2479
+ >
2480
+ > {
2481
+ log.info(`Diagnosing server: ${query}`);
2482
+
2483
+ const serverResult = await this.resolveServer(query);
2484
+ if (isErr(serverResult)) return err(serverResult.error);
2485
+
2486
+ const server = serverResult.value;
2487
+ const issues: string[] = [];
2488
+
2489
+ const [resourcesResult, domainsResult] = await Promise.all([
2490
+ this.getServerResources(server.uuid),
2491
+ this.getServerDomains(server.uuid),
2492
+ ]);
2493
+
2494
+ const resources = isErr(resourcesResult) ? [] : resourcesResult.value;
2495
+ const domains = isErr(domainsResult) ? [] : domainsResult.value;
2496
+
2497
+ if (!server.is_reachable) {
2498
+ issues.push("Server is not reachable");
2499
+ }
2500
+ if (!server.is_usable) {
2501
+ issues.push("Server is not usable");
2502
+ }
2503
+ if (resources.length === 0) {
2504
+ issues.push("No resources deployed on this server");
2505
+ }
2506
+
2507
+ log.success(`Server diagnosis complete for ${server.name}`);
2508
+ return ok({ server, resources, domains, issues });
2509
+ }
2510
+
2511
+ /**
2512
+ * Scans all infrastructure for potential issues.
2513
+ *
2514
+ * @returns Result with issues report or error
2515
+ */
2516
+ async findInfrastructureIssues(): Promise<
2517
+ Result<
2518
+ {
2519
+ totalServers: number;
2520
+ totalApps: number;
2521
+ totalDatabases: number;
2522
+ totalServices: number;
2523
+ issues: Array<{
2524
+ type: string;
2525
+ resource: string;
2526
+ uuid: string;
2527
+ message: string;
2528
+ }>;
2529
+ },
2530
+ Error
2531
+ >
2532
+ > {
2533
+ log.info("Scanning infrastructure for issues");
2534
+
2535
+ const [serversR, appsR, dbsR, svcsR] = await Promise.all([
2536
+ this.listServers(),
2537
+ this.listApplications(),
2538
+ this.listDatabases(),
2539
+ this.listServices(),
2540
+ ]);
2541
+
2542
+ const servers = isErr(serversR) ? [] : serversR.value;
2543
+ const apps = isErr(appsR) ? [] : appsR.value;
2544
+ const dbs = isErr(dbsR) ? [] : dbsR.value;
2545
+ const svcs = isErr(svcsR) ? [] : svcsR.value;
2546
+
2547
+ const issues: Array<{
2548
+ type: string;
2549
+ resource: string;
2550
+ uuid: string;
2551
+ message: string;
2552
+ }> = [];
2553
+
2554
+ for (const server of servers) {
2555
+ if (!server.is_reachable) {
2556
+ issues.push({
2557
+ type: "server",
2558
+ resource: server.name,
2559
+ uuid: server.uuid,
2560
+ message: "Server unreachable",
2561
+ });
2562
+ }
2563
+ }
2564
+
2565
+ for (const app of apps) {
2566
+ if (app.status?.includes("stopped")) {
2567
+ issues.push({
2568
+ type: "application",
2569
+ resource: app.name,
2570
+ uuid: app.uuid,
2571
+ message: "Application stopped",
2572
+ });
2573
+ }
2574
+ if (app.status?.includes("failed") || app.status?.includes("error")) {
2575
+ issues.push({
2576
+ type: "application",
2577
+ resource: app.name,
2578
+ uuid: app.uuid,
2579
+ message: `Status: ${app.status}`,
2580
+ });
2581
+ }
2582
+ }
2583
+
2584
+ for (const db of dbs) {
2585
+ if (db.status?.includes("stopped") || db.status?.includes("exited")) {
2586
+ issues.push({
2587
+ type: "database",
2588
+ resource: db.name,
2589
+ uuid: db.uuid,
2590
+ message: `Database stopped: ${db.status}`,
2591
+ });
2592
+ }
2593
+ }
2594
+
2595
+ for (const svc of svcs) {
2596
+ if (svc.status?.includes("stopped") || svc.status?.includes("exited")) {
2597
+ issues.push({
2598
+ type: "service",
2599
+ resource: svc.name,
2600
+ uuid: svc.uuid,
2601
+ message: `Service stopped: ${svc.status}`,
2602
+ });
2603
+ }
2604
+ }
2605
+
2606
+ log.success(
2607
+ `Infrastructure scan complete: ${issues.length} issue(s) found`,
2608
+ );
2609
+ return ok({
2610
+ totalServers: servers.length,
2611
+ totalApps: apps.length,
2612
+ totalDatabases: dbs.length,
2613
+ totalServices: svcs.length,
2614
+ issues,
2615
+ });
2616
+ }
2617
+
2618
+ // ===========================================================================
2619
+ // Batch Operations
2620
+ // ===========================================================================
2621
+
2622
+ /**
2623
+ * Restarts all applications in a project.
2624
+ *
2625
+ * @param projectUuid - Project UUID
2626
+ * @returns Result with batch operation results
2627
+ */
2628
+ async restartProjectApps(
2629
+ projectUuid: string,
2630
+ ): Promise<
2631
+ Result<{ total: number; succeeded: number; failed: string[] }, Error>
2632
+ > {
2633
+ log.info(`Restarting all apps in project ${projectUuid}`);
2634
+
2635
+ const appsResult = await this.listApplications(undefined, projectUuid);
2636
+ if (isErr(appsResult)) return err(appsResult.error);
2637
+
2638
+ const apps = appsResult.value;
2639
+ const failed: string[] = [];
2640
+ let succeeded = 0;
2641
+
2642
+ for (const app of apps) {
2643
+ const result = await this.restartApplication(app.uuid);
2644
+ if (isErr(result)) {
2645
+ failed.push(`${app.name} (${app.uuid}): ${result.error.message}`);
2646
+ } else {
2647
+ succeeded++;
2648
+ }
2649
+ }
2650
+
2651
+ log.success(`Restarted ${succeeded}/${apps.length} apps in project`);
2652
+ return ok({ total: apps.length, succeeded, failed });
2653
+ }
2654
+
2655
+ /**
2656
+ * Redeploys all applications in a project.
2657
+ *
2658
+ * @param projectUuid - Project UUID
2659
+ * @param force - Force rebuild
2660
+ * @returns Result with batch operation results
2661
+ */
2662
+ async redeployProjectApps(
2663
+ projectUuid: string,
2664
+ force: boolean = false,
2665
+ ): Promise<
2666
+ Result<{ total: number; succeeded: number; failed: string[] }, Error>
2667
+ > {
2668
+ log.info(`Redeploying all apps in project ${projectUuid}`);
2669
+
2670
+ const appsResult = await this.listApplications(undefined, projectUuid);
2671
+ if (isErr(appsResult)) return err(appsResult.error);
2672
+
2673
+ const apps = appsResult.value;
2674
+ const failed: string[] = [];
2675
+ let succeeded = 0;
2676
+
2677
+ for (const app of apps) {
2678
+ const result = await this.deploy({ uuid: app.uuid, force });
2679
+ if (isErr(result)) {
2680
+ failed.push(`${app.name} (${app.uuid}): ${result.error.message}`);
2681
+ } else {
2682
+ succeeded++;
2683
+ }
2684
+ }
2685
+
2686
+ log.success(`Redeployed ${succeeded}/${apps.length} apps in project`);
2687
+ return ok({ total: apps.length, succeeded, failed });
2688
+ }
2689
+
2690
+ /**
2691
+ * Stops all running applications.
2692
+ *
2693
+ * @returns Result with batch operation results
2694
+ */
2695
+ async stopAllApps(): Promise<
2696
+ Result<{ total: number; succeeded: number; failed: string[] }, Error>
2697
+ > {
2698
+ log.info("Stopping all applications");
2699
+
2700
+ const appsResult = await this.listApplications();
2701
+ if (isErr(appsResult)) return err(appsResult.error);
2702
+
2703
+ const running = appsResult.value.filter(
2704
+ (a) => !a.status?.includes("stopped"),
2705
+ );
2706
+ const failed: string[] = [];
2707
+ let succeeded = 0;
2708
+
2709
+ for (const app of running) {
2710
+ const result = await this.stopApplication(app.uuid);
2711
+ if (isErr(result)) {
2712
+ failed.push(`${app.name} (${app.uuid}): ${result.error.message}`);
2713
+ } else {
2714
+ succeeded++;
2715
+ }
992
2716
  }
993
2717
 
994
- const deployments = result.data?.deployments || []
995
- log.success(`Retrieved ${deployments.length} deployments for ${appUuid}`)
996
- return ok(deployments)
2718
+ log.success(`Stopped ${succeeded}/${running.length} apps`);
2719
+ return ok({ total: running.length, succeeded, failed });
2720
+ }
2721
+
2722
+ // ===========================================================================
2723
+ // Summary Types (Token Optimization for MCP)
2724
+ // ===========================================================================
2725
+
2726
+ /**
2727
+ * Lists applications with minimal fields for token efficiency.
2728
+ *
2729
+ * @returns Result with application summaries or error
2730
+ */
2731
+ async listApplicationSummaries(): Promise<
2732
+ Result<
2733
+ Array<{
2734
+ uuid: string;
2735
+ name: string;
2736
+ status: string;
2737
+ fqdn: string | null;
2738
+ }>,
2739
+ Error
2740
+ >
2741
+ > {
2742
+ const result = await this.listApplications();
2743
+ if (isErr(result)) return err(result.error);
2744
+
2745
+ return ok(
2746
+ result.value.map((a) => ({
2747
+ uuid: a.uuid,
2748
+ name: a.name,
2749
+ status: a.status,
2750
+ fqdn: a.fqdn || null,
2751
+ })),
2752
+ );
2753
+ }
2754
+
2755
+ /**
2756
+ * Lists servers with minimal fields for token efficiency.
2757
+ *
2758
+ * @returns Result with server summaries or error
2759
+ */
2760
+ async listServerSummaries(): Promise<
2761
+ Result<
2762
+ Array<{
2763
+ uuid: string;
2764
+ name: string;
2765
+ ip: string;
2766
+ is_reachable: boolean;
2767
+ }>,
2768
+ Error
2769
+ >
2770
+ > {
2771
+ const result = await this.listServers();
2772
+ if (isErr(result)) return err(result.error);
2773
+
2774
+ return ok(
2775
+ result.value.map((s) => ({
2776
+ uuid: s.uuid,
2777
+ name: s.name,
2778
+ ip: s.ip || "",
2779
+ is_reachable: s.is_reachable || false,
2780
+ })),
2781
+ );
2782
+ }
2783
+
2784
+ /**
2785
+ * Lists databases with minimal fields for token efficiency.
2786
+ *
2787
+ * @returns Result with database summaries or error
2788
+ */
2789
+ async listDatabaseSummaries(): Promise<
2790
+ Result<
2791
+ Array<{
2792
+ uuid: string;
2793
+ name: string;
2794
+ type: string;
2795
+ status: string;
2796
+ }>,
2797
+ Error
2798
+ >
2799
+ > {
2800
+ const result = await this.listDatabases();
2801
+ if (isErr(result)) return err(result.error);
2802
+
2803
+ return ok(
2804
+ result.value.map((d) => ({
2805
+ uuid: d.uuid,
2806
+ name: d.name,
2807
+ type: d.type,
2808
+ status: d.status,
2809
+ })),
2810
+ );
2811
+ }
2812
+
2813
+ /**
2814
+ * Lists services with minimal fields for token efficiency.
2815
+ *
2816
+ * @returns Result with service summaries or error
2817
+ */
2818
+ async listServiceSummaries(): Promise<
2819
+ Result<
2820
+ Array<{
2821
+ uuid: string;
2822
+ name: string;
2823
+ type: string;
2824
+ status: string;
2825
+ }>,
2826
+ Error
2827
+ >
2828
+ > {
2829
+ const result = await this.listServices();
2830
+ if (isErr(result)) return err(result.error);
2831
+
2832
+ return ok(
2833
+ result.value.map((s) => ({
2834
+ uuid: s.uuid,
2835
+ name: s.name,
2836
+ type: s.type,
2837
+ status: s.status,
2838
+ })),
2839
+ );
997
2840
  }
998
2841
  }
999
2842
 
1000
- let instance: CoolifyService | null = null
2843
+ let instance: CoolifyService | null = null;
1001
2844
 
1002
2845
  /**
1003
2846
  * Gets the singleton CoolifyService instance.
@@ -1006,19 +2849,26 @@ let instance: CoolifyService | null = null
1006
2849
  */
1007
2850
  export function getCoolifyService(): CoolifyService {
1008
2851
  if (!instance) {
1009
- instance = new CoolifyService()
2852
+ instance = new CoolifyService();
1010
2853
  }
1011
- return instance
2854
+ return instance;
1012
2855
  }
1013
2856
 
1014
2857
  // Re-export types
1015
2858
  export type {
1016
2859
  ICoolifyServer,
2860
+ ICoolifyServerResource,
2861
+ ICoolifyServerDomain,
1017
2862
  ICoolifyDestination,
1018
2863
  ICoolifyProject,
1019
2864
  ICoolifyTeam,
1020
2865
  ICoolifyApplication,
2866
+ ICoolifyDatabase,
2867
+ ICoolifyDatabaseBackup,
2868
+ ICoolifyService as ICoolifyServiceType,
2869
+ ICoolifyPrivateKey,
1021
2870
  ICoolifyDeployment,
2871
+ ICoolifyVersion,
1022
2872
  ICoolifyAppOptions,
1023
2873
  ICoolifyAppResult,
1024
2874
  ICoolifyDeployOptions,
@@ -1028,4 +2878,4 @@ export type {
1028
2878
  ICoolifyLogsOptions,
1029
2879
  ICoolifyLogs,
1030
2880
  IProgressCallback,
1031
- } from './types.js'
2881
+ } from "./types.js";