@mks2508/coolify-mks-cli-mcp 0.1.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 (42) hide show
  1. package/dist/cli/index.js +11788 -0
  2. package/dist/coolify/config.d.ts +64 -0
  3. package/dist/coolify/config.d.ts.map +1 -0
  4. package/dist/coolify/index.d.ts +201 -0
  5. package/dist/coolify/index.d.ts.map +1 -0
  6. package/dist/coolify/types.d.ts +282 -0
  7. package/dist/coolify/types.d.ts.map +1 -0
  8. package/dist/index.cjs +29150 -0
  9. package/dist/index.cjs.map +1 -0
  10. package/dist/index.d.ts +14 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +29127 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/server/sse.d.ts +11 -0
  15. package/dist/server/sse.d.ts.map +1 -0
  16. package/dist/server/sse.js +32 -0
  17. package/dist/server/stdio.d.ts +13 -0
  18. package/dist/server/stdio.d.ts.map +1 -0
  19. package/dist/server/stdio.js +18326 -0
  20. package/dist/tools/definitions.d.ts +13 -0
  21. package/dist/tools/definitions.d.ts.map +1 -0
  22. package/dist/tools/handlers.d.ts +19 -0
  23. package/dist/tools/handlers.d.ts.map +1 -0
  24. package/dist/utils/format.d.ts +38 -0
  25. package/dist/utils/format.d.ts.map +1 -0
  26. package/package.json +67 -0
  27. package/src/cli/commands/config.ts +83 -0
  28. package/src/cli/commands/deploy.ts +56 -0
  29. package/src/cli/commands/env.ts +60 -0
  30. package/src/cli/commands/list.ts +63 -0
  31. package/src/cli/commands/logs.ts +49 -0
  32. package/src/cli/commands/servers.ts +52 -0
  33. package/src/cli/index.ts +81 -0
  34. package/src/coolify/config.ts +113 -0
  35. package/src/coolify/index.ts +688 -0
  36. package/src/coolify/types.ts +297 -0
  37. package/src/index.ts +864 -0
  38. package/src/server/sse.ts +50 -0
  39. package/src/server/stdio.ts +52 -0
  40. package/src/tools/definitions.ts +435 -0
  41. package/src/tools/handlers.ts +605 -0
  42. package/src/utils/format.ts +104 -0
@@ -0,0 +1,688 @@
1
+ /**
2
+ * Coolify service for MCP server and CLI.
3
+ *
4
+ * Provides all Coolify API operations for deployment management.
5
+ *
6
+ * @module
7
+ */
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'
12
+ import {
13
+ type ICoolifyAppOptions,
14
+ type ICoolifyAppResult,
15
+ type ICoolifyApplication,
16
+ type ICoolifyDeleteResult,
17
+ type ICoolifyDeployment,
18
+ type ICoolifyDeployOptions,
19
+ type ICoolifyDeployResult,
20
+ type ICoolifyDestination,
21
+ type ICoolifyLogs,
22
+ type ICoolifyLogsOptions,
23
+ type ICoolifyProject,
24
+ type ICoolifyServer,
25
+ type ICoolifyTeam,
26
+ type ICoolifyUpdateOptions,
27
+ type IProgressCallback,
28
+ } from './types.js'
29
+
30
+ const log = component('CoolifyService')
31
+
32
+ /**
33
+ * Coolify API response type.
34
+ */
35
+ interface ICoolifyApiResponse<T> {
36
+ data?: T
37
+ error?: string
38
+ status: number
39
+ durationMs?: number
40
+ }
41
+
42
+ /**
43
+ * Environment variable from Coolify API.
44
+ */
45
+ export interface ICoolifyEnvVar {
46
+ uuid: string
47
+ key: string
48
+ value: string
49
+ real_value?: string
50
+ is_buildtime: boolean
51
+ is_runtime: boolean
52
+ is_required: boolean
53
+ }
54
+
55
+ /**
56
+ * Coolify service for deployment operations.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const coolify = new CoolifyService()
61
+ * const initResult = await coolify.init()
62
+ * if (initResult.isErr()) {
63
+ * console.error(initResult.error.message)
64
+ * return
65
+ * }
66
+ *
67
+ * const deployResult = await coolify.deploy({ uuid: 'app-uuid' })
68
+ * if (deployResult.isOk()) {
69
+ * console.log('Deployment UUID:', deployResult.value.deploymentUuid)
70
+ * }
71
+ * ```
72
+ */
73
+ export class CoolifyService {
74
+ private baseUrl: string | undefined
75
+ private token: string | undefined
76
+ private config: ICoolifyConfig = {}
77
+
78
+ /**
79
+ * Checks if the service is configured with URL and token.
80
+ *
81
+ * @returns true if both URL and token are set
82
+ */
83
+ isConfigured(): boolean {
84
+ const hasUrl = !!this.baseUrl || !!process.env.COOLIFY_URL
85
+ const hasToken = !!this.token || !!process.env.COOLIFY_TOKEN
86
+ return hasUrl && hasToken
87
+ }
88
+
89
+ /**
90
+ * Initializes the Coolify service by loading configuration.
91
+ *
92
+ * @returns Result indicating success or error
93
+ */
94
+ async init(): Promise<Result<void, Error>> {
95
+ const configResult = await loadConfig()
96
+
97
+ if (isErr(configResult)) {
98
+ log.error('Failed to load config')
99
+ return err(configResult.error)
100
+ }
101
+
102
+ this.config = configResult.value
103
+ this.baseUrl = this.config.url || process.env.COOLIFY_URL
104
+ this.token = this.config.token || process.env.COOLIFY_TOKEN
105
+
106
+ if (!this.baseUrl) {
107
+ log.error('No Coolify URL configured')
108
+ log.info('Set COOLIFY_URL environment variable or run: coolify-mcp config set url <url>')
109
+ return err(new Error('No Coolify URL configured. Set COOLIFY_URL or use config command.'))
110
+ }
111
+
112
+ if (!this.token) {
113
+ log.error('No Coolify token configured')
114
+ log.info('Set COOLIFY_TOKEN environment variable or run: coolify-mcp config set token <token>')
115
+ return err(new Error('No Coolify token configured. Set COOLIFY_TOKEN or use config command.'))
116
+ }
117
+
118
+ log.debug('Coolify connection configured')
119
+ return ok(undefined)
120
+ }
121
+
122
+ /**
123
+ * Makes a request to the Coolify API.
124
+ *
125
+ * @param endpoint - API endpoint
126
+ * @param options - Fetch options
127
+ * @returns API response with data or error
128
+ */
129
+ private async request<T>(
130
+ endpoint: string,
131
+ options: RequestInit = {}
132
+ ): Promise<ICoolifyApiResponse<T>> {
133
+ const startTime = Date.now()
134
+
135
+ if (!this.baseUrl || !this.token) {
136
+ return { error: 'Coolify not configured', status: 0, durationMs: Date.now() - startTime }
137
+ }
138
+
139
+ try {
140
+ const url = `${this.baseUrl}/api/v1${endpoint}`
141
+ const response = await fetch(url, {
142
+ ...options,
143
+ headers: {
144
+ Authorization: `Bearer ${this.token}`,
145
+ 'Content-Type': 'application/json',
146
+ Accept: 'application/json',
147
+ ...options.headers,
148
+ },
149
+ })
150
+
151
+ const text = await response.text()
152
+ const durationMs = Date.now() - startTime
153
+ let data: T | undefined
154
+
155
+ try {
156
+ data = text ? JSON.parse(text) : undefined
157
+ } catch {
158
+ if (!response.ok) {
159
+ return { error: text || `HTTP ${response.status}`, status: response.status, durationMs }
160
+ }
161
+ }
162
+
163
+ if (!response.ok) {
164
+ const errorMessage =
165
+ (data as { message?: string } | undefined)?.message ||
166
+ `HTTP ${response.status}`
167
+ return { error: errorMessage, status: response.status, durationMs }
168
+ }
169
+
170
+ return { data, status: response.status, durationMs }
171
+ } catch (error) {
172
+ const message = error instanceof Error ? error.message : 'Unknown error'
173
+ return { error: message, status: 0, durationMs: Date.now() - startTime }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Deploys an application.
179
+ *
180
+ * @param options - Deployment options
181
+ * @param onProgress - Optional progress callback (0-100, message, step)
182
+ * @returns Result with deployment info or error
183
+ */
184
+ async deploy(
185
+ options: ICoolifyDeployOptions,
186
+ onProgress?: IProgressCallback
187
+ ): Promise<Result<ICoolifyDeployResult, Error>> {
188
+ if (!options.uuid && !options.tag) {
189
+ return err(new Error('Either uuid or tag is required'))
190
+ }
191
+
192
+ const appId = options.uuid?.slice(0, 8) || options.tag || 'unknown'
193
+ onProgress?.(5, `Preparing deployment for ${appId}...`)
194
+
195
+ log.info(`Deploying application ${options.uuid || options.tag}`)
196
+
197
+ onProgress?.(25, 'Validating deployment configuration')
198
+ onProgress?.(50, 'Triggering build pipeline...')
199
+
200
+ const result = await this.request<{
201
+ resource_uuid: string
202
+ deployment_uuid: string
203
+ }>('/deploy', {
204
+ method: 'POST',
205
+ body: JSON.stringify({
206
+ uuid: options.uuid,
207
+ tag: options.tag,
208
+ force: options.force ?? false,
209
+ }),
210
+ })
211
+
212
+ if (result.error) {
213
+ log.error(`Deployment failed: ${result.error}`)
214
+ return err(new Error(result.error))
215
+ }
216
+
217
+ onProgress?.(90, 'Build started on Coolify server')
218
+ onProgress?.(100, 'Deployment triggered')
219
+
220
+ log.success(`Deployment started: ${result.data?.deployment_uuid}`)
221
+ return ok({
222
+ success: true,
223
+ deploymentUuid: result.data?.deployment_uuid,
224
+ resourceUuid: result.data?.resource_uuid,
225
+ })
226
+ }
227
+
228
+ /**
229
+ * Creates a new application in Coolify.
230
+ *
231
+ * @param options - Application options
232
+ * @param onProgress - Optional progress callback (0-100, message, step)
233
+ * @returns Result with application UUID or error
234
+ */
235
+ async createApplication(
236
+ options: ICoolifyAppOptions,
237
+ onProgress?: IProgressCallback
238
+ ): Promise<Result<ICoolifyAppResult, Error>> {
239
+ onProgress?.(5, `Preparing app "${options.name}"`)
240
+
241
+ log.info(`Creating application ${options.name}`)
242
+
243
+ onProgress?.(25, `Validating server ${options.serverUuid.slice(0, 8)}...`)
244
+ onProgress?.(50, 'Sending creation request to Coolify API...')
245
+
246
+ const result = await this.request<{ uuid: string }>('/applications', {
247
+ method: 'POST',
248
+ body: JSON.stringify({
249
+ name: options.name,
250
+ description: options.description,
251
+ server_uuid: options.serverUuid,
252
+ destination_uuid: options.destinationUuid,
253
+ project_uuid: options.serverUuid,
254
+ environment_name: 'production',
255
+ git_repository: options.githubRepoUrl,
256
+ git_branch: options.branch || 'main',
257
+ build_pack: options.buildPack || 'nixpacks',
258
+ ports_exposes: '3000',
259
+ instant_deploy: false,
260
+ }),
261
+ })
262
+
263
+ if (result.error) {
264
+ log.error(`Failed to create application: ${result.error}`)
265
+ return err(new Error(result.error))
266
+ }
267
+
268
+ onProgress?.(100, `Application "${options.name}" created`)
269
+
270
+ log.success(`Application created: ${result.data?.uuid}`)
271
+ return ok({
272
+ success: true,
273
+ uuid: result.data?.uuid,
274
+ })
275
+ }
276
+
277
+ /**
278
+ * Sets environment variables for an application.
279
+ *
280
+ * @param appUuid - Application UUID
281
+ * @param envVars - Environment variables to set
282
+ * @returns Result indicating success or error
283
+ */
284
+ async setEnvironmentVariables(
285
+ appUuid: string,
286
+ envVars: Record<string, string>
287
+ ): Promise<Result<void, Error>> {
288
+ log.info(`Setting environment variables for ${appUuid}`)
289
+
290
+ const envArray = Object.entries(envVars).map(([key, value]) => ({
291
+ key,
292
+ value,
293
+ is_build_time: false,
294
+ }))
295
+
296
+ const result = await this.request(`/applications/${appUuid}/envs`, {
297
+ method: 'POST',
298
+ body: JSON.stringify({ data: envArray }),
299
+ })
300
+
301
+ if (result.error) {
302
+ log.error(`Failed to set env vars: ${result.error}`)
303
+ return err(new Error(result.error))
304
+ }
305
+
306
+ log.success('Environment variables set')
307
+ return ok(undefined)
308
+ }
309
+
310
+ /**
311
+ * Gets environment variables for an application.
312
+ *
313
+ * @param appUuid - Application UUID
314
+ * @returns Result with environment variables or error
315
+ */
316
+ async getEnvironmentVariables(
317
+ appUuid: string
318
+ ): Promise<Result<ICoolifyEnvVar[], Error>> {
319
+ log.info(`Getting environment variables for ${appUuid}`)
320
+
321
+ const result = await this.request<ICoolifyEnvVar[]>(
322
+ `/applications/${appUuid}/envs`
323
+ )
324
+
325
+ if (result.error) {
326
+ log.error(`Failed to get env vars: ${result.error}`)
327
+ return err(new Error(result.error))
328
+ }
329
+
330
+ log.success(`Environment variables retrieved for ${appUuid}`)
331
+ return ok(result.data || [])
332
+ }
333
+
334
+ /**
335
+ * Gets the status of an application.
336
+ *
337
+ * @param appUuid - Application UUID
338
+ * @returns Result with status or error
339
+ */
340
+ async getApplicationStatus(
341
+ appUuid: string
342
+ ): Promise<Result<string, Error>> {
343
+ const result = await this.request<{ status: string }>(
344
+ `/applications/${appUuid}`
345
+ )
346
+
347
+ if (result.error) {
348
+ return err(new Error(result.error))
349
+ }
350
+
351
+ return ok(result.data?.status || 'unknown')
352
+ }
353
+
354
+ /**
355
+ * Lists available servers in Coolify.
356
+ *
357
+ * @returns Result with servers or error
358
+ */
359
+ async listServers(): Promise<Result<ICoolifyServer[], Error>> {
360
+ const result = await this.request<ICoolifyServer[]>('/servers')
361
+
362
+ if (result.error) {
363
+ return err(new Error(result.error))
364
+ }
365
+
366
+ return ok(result.data || [])
367
+ }
368
+
369
+ /**
370
+ * Gets details of a specific server.
371
+ *
372
+ * @param serverUuid - Server UUID
373
+ * @returns Result with server details or error
374
+ */
375
+ async getServer(
376
+ serverUuid: string
377
+ ): Promise<Result<ICoolifyServer, Error>> {
378
+ log.info(`Getting server details for ${serverUuid}`)
379
+
380
+ const result = await this.request<ICoolifyServer>(`/servers/${serverUuid}`)
381
+
382
+ if (result.error) {
383
+ log.error(`Failed to get server: ${result.error}`)
384
+ return err(new Error(result.error))
385
+ }
386
+
387
+ log.success(`Server details retrieved: ${serverUuid}`)
388
+ return ok(result.data as ICoolifyServer)
389
+ }
390
+
391
+ /**
392
+ * Lists all projects.
393
+ *
394
+ * @returns Result with projects list or error
395
+ */
396
+ async listProjects(): Promise<Result<ICoolifyProject[], Error>> {
397
+ const result = await this.request<ICoolifyProject[]>('/projects')
398
+
399
+ if (result.error) {
400
+ return err(new Error(result.error))
401
+ }
402
+
403
+ return ok(result.data || [])
404
+ }
405
+
406
+ /**
407
+ * Lists all teams.
408
+ *
409
+ * @returns Result with teams list or error
410
+ */
411
+ async listTeams(): Promise<Result<ICoolifyTeam[], Error>> {
412
+ const result = await this.request<ICoolifyTeam[]>('/teams')
413
+
414
+ if (result.error) {
415
+ return err(new Error(result.error))
416
+ }
417
+
418
+ return ok(result.data || [])
419
+ }
420
+
421
+ /**
422
+ * Gets available destinations for a server.
423
+ *
424
+ * @param serverUuid - Server UUID
425
+ * @returns Result with destinations or error
426
+ */
427
+ async getServerDestinations(
428
+ serverUuid: string
429
+ ): Promise<Result<ICoolifyDestination[], Error>> {
430
+ const result = await this.request<{
431
+ destinations: ICoolifyDestination[]
432
+ }>(`/servers/${serverUuid}`)
433
+
434
+ if (result.error) {
435
+ return err(new Error(result.error))
436
+ }
437
+
438
+ return ok(result.data?.destinations || [])
439
+ }
440
+
441
+ /**
442
+ * Lists all applications.
443
+ *
444
+ * @param teamId - Optional team ID to filter by
445
+ * @param projectId - Optional project ID to filter by
446
+ * @returns Result with applications list or error
447
+ */
448
+ async listApplications(
449
+ teamId?: string,
450
+ projectId?: string
451
+ ): Promise<Result<ICoolifyApplication[], Error>> {
452
+ log.info('Listing applications')
453
+
454
+ let endpoint = '/applications'
455
+ const params = new URLSearchParams()
456
+ if (teamId) params.set('team_id', teamId)
457
+ if (projectId) params.set('project_id', projectId)
458
+ if (params.toString()) {
459
+ endpoint += `?${params.toString()}`
460
+ }
461
+
462
+ const result = await this.request<ICoolifyApplication[]>(endpoint)
463
+
464
+ if (result.error) {
465
+ log.error(`Failed to list applications: ${result.error}`)
466
+ return err(new Error(result.error))
467
+ }
468
+
469
+ log.success(`Listed ${result.data?.length || 0} applications`)
470
+ return ok(result.data || [])
471
+ }
472
+
473
+ /**
474
+ * Deletes an application.
475
+ *
476
+ * @param appUuid - Application UUID
477
+ * @returns Result indicating success or error
478
+ */
479
+ async deleteApplication(
480
+ appUuid: string
481
+ ): Promise<Result<ICoolifyDeleteResult, Error>> {
482
+ log.info(`Deleting application ${appUuid}`)
483
+
484
+ const result = await this.request<ICoolifyDeleteResult>(`/applications/${appUuid}`, {
485
+ method: 'DELETE',
486
+ })
487
+
488
+ if (result.error) {
489
+ log.error(`Failed to delete application: ${result.error}`)
490
+ return err(new Error(result.error))
491
+ }
492
+
493
+ log.success(`Application deleted: ${appUuid}`)
494
+ return ok({ success: true, message: 'Application deleted' })
495
+ }
496
+
497
+ /**
498
+ * Updates an application configuration.
499
+ *
500
+ * @param appUuid - Application UUID
501
+ * @param options - Update options
502
+ * @returns Result with updated application or error
503
+ */
504
+ async updateApplication(
505
+ appUuid: string,
506
+ options: ICoolifyUpdateOptions
507
+ ): Promise<Result<ICoolifyApplication, Error>> {
508
+ log.info(`Updating application ${appUuid}`)
509
+
510
+ const body: Record<string, unknown> = {}
511
+ if (options.name) body.name = options.name
512
+ if (options.description) body.description = options.description
513
+ if (options.buildPack) body.build_pack = options.buildPack
514
+ if (options.gitBranch) body.git_branch = options.gitBranch
515
+ if (options.portsExposes) body.ports_exposes = options.portsExposes
516
+ if (options.installCommand) body.install_command = options.installCommand
517
+ if (options.buildCommand) body.build_command = options.buildCommand
518
+ if (options.startCommand) body.start_command = options.startCommand
519
+
520
+ const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}`, {
521
+ method: 'PATCH',
522
+ body: JSON.stringify(body),
523
+ })
524
+
525
+ if (result.error) {
526
+ log.error(`Failed to update application: ${result.error}`)
527
+ return err(new Error(result.error))
528
+ }
529
+
530
+ log.success(`Application updated: ${appUuid}`)
531
+ return ok(result.data as ICoolifyApplication)
532
+ }
533
+
534
+ /**
535
+ * Gets application logs.
536
+ *
537
+ * @param appUuid - Application UUID
538
+ * @param options - Log retrieval options
539
+ * @returns Result with logs or error
540
+ */
541
+ async getApplicationLogs(
542
+ appUuid: string,
543
+ options: ICoolifyLogsOptions = {}
544
+ ): Promise<Result<ICoolifyLogs, Error>> {
545
+ log.info(`Getting logs for application ${appUuid}`)
546
+
547
+ const params = new URLSearchParams()
548
+ if (options.follow) params.set('follow', 'true')
549
+ if (options.tail) params.set('tail', options.tail.toString())
550
+
551
+ const endpoint = `/applications/${appUuid}/logs${params.toString() ? `?${params.toString()}` : ''}`
552
+
553
+ const result = await this.request<{ logs: string[] }>(endpoint)
554
+
555
+ if (result.error) {
556
+ log.error(`Failed to get logs: ${result.error}`)
557
+ return err(new Error(result.error))
558
+ }
559
+
560
+ log.success(`Logs retrieved for application: ${appUuid}`)
561
+ return ok({
562
+ logs: result.data?.logs || [],
563
+ timestamp: new Date().toISOString(),
564
+ })
565
+ }
566
+
567
+ /**
568
+ * Gets deployment history for an application.
569
+ *
570
+ * @param appUuid - Application UUID
571
+ * @returns Result with deployment history or error
572
+ */
573
+ async getApplicationDeploymentHistory(
574
+ appUuid: string
575
+ ): Promise<Result<ICoolifyDeployment[], Error>> {
576
+ log.info(`Getting deployment history for ${appUuid}`)
577
+
578
+ const result = await this.request<ICoolifyDeployment[]>(`/applications/${appUuid}/deployments`)
579
+
580
+ if (result.error) {
581
+ log.error(`Failed to get deployment history: ${result.error}`)
582
+ return err(new Error(result.error))
583
+ }
584
+
585
+ log.success(`Deployment history retrieved for ${appUuid}`)
586
+ return ok(result.data || [])
587
+ }
588
+
589
+ /**
590
+ * Starts a stopped application.
591
+ *
592
+ * @param appUuid - Application UUID
593
+ * @returns Result with application status or error
594
+ */
595
+ async startApplication(
596
+ appUuid: string
597
+ ): Promise<Result<ICoolifyApplication, Error>> {
598
+ log.info(`Starting application ${appUuid}`)
599
+
600
+ const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}/start`, {
601
+ method: 'POST',
602
+ })
603
+
604
+ if (result.error) {
605
+ log.error(`Failed to start application: ${result.error}`)
606
+ return err(new Error(result.error))
607
+ }
608
+
609
+ log.success(`Application started: ${appUuid}`)
610
+ return ok(result.data as ICoolifyApplication)
611
+ }
612
+
613
+ /**
614
+ * Stops a running application.
615
+ *
616
+ * @param appUuid - Application UUID
617
+ * @returns Result with application status or error
618
+ */
619
+ async stopApplication(
620
+ appUuid: string
621
+ ): Promise<Result<ICoolifyApplication, Error>> {
622
+ log.info(`Stopping application ${appUuid}`)
623
+
624
+ const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}/stop`, {
625
+ method: 'POST',
626
+ })
627
+
628
+ if (result.error) {
629
+ log.error(`Failed to stop application: ${result.error}`)
630
+ return err(new Error(result.error))
631
+ }
632
+
633
+ log.success(`Application stopped: ${appUuid}`)
634
+ return ok(result.data as ICoolifyApplication)
635
+ }
636
+
637
+ /**
638
+ * Restarts an application.
639
+ *
640
+ * @param appUuid - Application UUID
641
+ * @returns Result with application status or error
642
+ */
643
+ async restartApplication(
644
+ appUuid: string
645
+ ): Promise<Result<ICoolifyApplication, Error>> {
646
+ log.info(`Restarting application ${appUuid}`)
647
+
648
+ const result = await this.request<ICoolifyApplication>(`/applications/${appUuid}/restart`, {
649
+ method: 'POST',
650
+ })
651
+
652
+ if (result.error) {
653
+ log.error(`Failed to restart application: ${result.error}`)
654
+ return err(new Error(result.error))
655
+ }
656
+
657
+ log.success(`Application restarted: ${appUuid}`)
658
+ return ok(result.data as ICoolifyApplication)
659
+ }
660
+ }
661
+
662
+ let instance: CoolifyService | null = null
663
+
664
+ /**
665
+ * Gets the singleton CoolifyService instance.
666
+ *
667
+ * @returns The CoolifyService instance
668
+ */
669
+ export function getCoolifyService(): CoolifyService {
670
+ if (!instance) {
671
+ instance = new CoolifyService()
672
+ }
673
+ return instance
674
+ }
675
+
676
+ // Re-export types
677
+ export type {
678
+ ICoolifyServer,
679
+ ICoolifyDestination,
680
+ ICoolifyProject,
681
+ ICoolifyTeam,
682
+ ICoolifyApplication,
683
+ ICoolifyDeployment,
684
+ ICoolifyAppOptions,
685
+ ICoolifyDeployOptions,
686
+ ICoolifyUpdateOptions,
687
+ ICoolifyLogsOptions,
688
+ }