@mks2508/coolify-mks-cli-mcp 0.5.0 → 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 +25 -22
  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 +34 -18
  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 +24 -16
  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 +34 -18
  68. package/src/cli/commands/stop.ts +34 -18
  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 +383 -151
  75. package/src/coolify/config.ts +29 -27
  76. package/src/coolify/index.ts +1829 -123
  77. package/src/coolify/types.ts +217 -124
  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
@@ -13,6 +13,8 @@ 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,10 +23,15 @@ 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
36
  } from "./types.js";
30
37
 
@@ -477,7 +484,7 @@ export class CoolifyService {
477
484
  appUuid: string,
478
485
  key: string,
479
486
  value: string,
480
- isBuildTime: boolean = false,
487
+ _isBuildTime: boolean = false,
481
488
  ): Promise<Result<void, Error>> {
482
489
  log.info(`Setting environment variable ${key} for ${appUuid}`);
483
490
 
@@ -587,10 +594,23 @@ export class CoolifyService {
587
594
  /**
588
595
  * Lists available servers in Coolify.
589
596
  *
597
+ * @param page - Optional page number for pagination
598
+ * @param perPage - Optional number of items per page
590
599
  * @returns Result with servers or error
591
600
  */
592
- async listServers(): Promise<Result<ICoolifyServer[], Error>> {
593
- 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);
594
614
 
595
615
  if (result.error) {
596
616
  return err(new Error(result.error));
@@ -622,18 +642,31 @@ export class CoolifyService {
622
642
  /**
623
643
  * Lists all GitHub Apps configured in Coolify.
624
644
  *
645
+ * @param page - Optional page number for pagination
646
+ * @param perPage - Optional number of items per page
625
647
  * @returns Result with GitHub Apps list or error
626
648
  */
627
- async listGithubApps(): Promise<
649
+ async listGithubApps(
650
+ page?: number,
651
+ perPage?: number,
652
+ ): Promise<
628
653
  Result<
629
654
  Array<{ id: number; uuid: string; name: string; is_public: boolean }>,
630
655
  Error
631
656
  >
632
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
+
633
666
  const result =
634
667
  await this.request<
635
668
  Array<{ id: number; uuid: string; name: string; is_public: boolean }>
636
- >("/github-apps");
669
+ >(endpoint);
637
670
  if (result.error) return err(new Error(result.error));
638
671
  return ok(result.data || []);
639
672
  }
@@ -641,10 +674,23 @@ export class CoolifyService {
641
674
  /**
642
675
  * Lists all projects.
643
676
  *
677
+ * @param page - Optional page number for pagination
678
+ * @param perPage - Optional number of items per page
644
679
  * @returns Result with projects list or error
645
680
  */
646
- async listProjects(): Promise<Result<ICoolifyProject[], Error>> {
647
- 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);
648
694
 
649
695
  if (result.error) {
650
696
  return err(new Error(result.error));
@@ -707,10 +753,23 @@ export class CoolifyService {
707
753
  /**
708
754
  * Lists all teams.
709
755
  *
756
+ * @param page - Optional page number for pagination
757
+ * @param perPage - Optional number of items per page
710
758
  * @returns Result with teams list or error
711
759
  */
712
- async listTeams(): Promise<Result<ICoolifyTeam[], Error>> {
713
- 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);
714
773
 
715
774
  if (result.error) {
716
775
  return err(new Error(result.error));
@@ -744,11 +803,15 @@ export class CoolifyService {
744
803
  *
745
804
  * @param teamId - Optional team ID to filter by
746
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
747
808
  * @returns Result with applications list or error
748
809
  */
749
810
  async listApplications(
750
811
  teamId?: string,
751
812
  projectId?: string,
813
+ page?: number,
814
+ perPage?: number,
752
815
  ): Promise<Result<ICoolifyApplication[], Error>> {
753
816
  log.info("Listing applications");
754
817
 
@@ -756,6 +819,8 @@ export class CoolifyService {
756
819
  const params = new URLSearchParams();
757
820
  if (teamId) params.set("team_id", teamId);
758
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());
759
824
  if (params.toString()) {
760
825
  endpoint += `?${params.toString()}`;
761
826
  }
@@ -775,19 +840,34 @@ export class CoolifyService {
775
840
  * Deletes an application.
776
841
  *
777
842
  * @param appUuid - Application UUID
843
+ * @param options - Delete options for cascade deletion
778
844
  * @returns Result indicating success or error
779
845
  */
780
846
  async deleteApplication(
781
847
  appUuid: string,
848
+ options?: {
849
+ deleteConfigurations?: boolean;
850
+ deleteVolumes?: boolean;
851
+ dockerCleanup?: boolean;
852
+ deleteConnectedNetworks?: boolean;
853
+ },
782
854
  ): Promise<Result<ICoolifyDeleteResult, Error>> {
783
855
  log.info(`Deleting application ${appUuid}`);
784
856
 
785
- const result = await this.request<ICoolifyDeleteResult>(
786
- `/applications/${appUuid}`,
787
- {
788
- method: "DELETE",
789
- },
790
- );
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
+ });
791
871
 
792
872
  if (result.error) {
793
873
  log.error(`Failed to delete application: ${result.error}`);
@@ -891,6 +971,70 @@ export class CoolifyService {
891
971
  });
892
972
  }
893
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" });
1036
+ }
1037
+
894
1038
  /**
895
1039
  * Gets deployment history for an application.
896
1040
  *
@@ -920,21 +1064,27 @@ export class CoolifyService {
920
1064
 
921
1065
  /**
922
1066
  * Starts a stopped application.
1067
+ * Note: Coolify API uses GET for application start/stop/restart.
923
1068
  *
924
1069
  * @param appUuid - Application UUID
1070
+ * @param options - Optional start options (force, instant_deploy)
925
1071
  * @returns Result with application status or error
926
1072
  */
927
1073
  async startApplication(
928
1074
  appUuid: string,
1075
+ options?: { force?: boolean; instantDeploy?: boolean },
929
1076
  ): Promise<Result<ICoolifyApplication, Error>> {
930
1077
  log.info(`Starting application ${appUuid}`);
931
1078
 
932
- const result = await this.request<ICoolifyApplication>(
933
- `/applications/${appUuid}/start`,
934
- {
935
- method: "POST",
936
- },
937
- );
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}` : ""}`;
1084
+
1085
+ const result = await this.request<ICoolifyApplication>(endpoint, {
1086
+ method: "GET",
1087
+ });
938
1088
 
939
1089
  if (result.error) {
940
1090
  log.error(`Failed to start application: ${result.error}`);
@@ -947,6 +1097,7 @@ export class CoolifyService {
947
1097
 
948
1098
  /**
949
1099
  * Stops a running application.
1100
+ * Note: Coolify API uses GET for application start/stop/restart.
950
1101
  *
951
1102
  * @param appUuid - Application UUID
952
1103
  * @returns Result with application status or error
@@ -958,9 +1109,7 @@ export class CoolifyService {
958
1109
 
959
1110
  const result = await this.request<ICoolifyApplication>(
960
1111
  `/applications/${appUuid}/stop`,
961
- {
962
- method: "POST",
963
- },
1112
+ { method: "GET" },
964
1113
  );
965
1114
 
966
1115
  if (result.error) {
@@ -974,6 +1123,7 @@ export class CoolifyService {
974
1123
 
975
1124
  /**
976
1125
  * Restarts an application.
1126
+ * Note: Coolify API uses GET for application start/stop/restart.
977
1127
  *
978
1128
  * @param appUuid - Application UUID
979
1129
  * @returns Result with application status or error
@@ -985,9 +1135,7 @@ export class CoolifyService {
985
1135
 
986
1136
  const result = await this.request<ICoolifyApplication>(
987
1137
  `/applications/${appUuid}/restart`,
988
- {
989
- method: "POST",
990
- },
1138
+ { method: "GET" },
991
1139
  );
992
1140
 
993
1141
  if (result.error) {
@@ -999,145 +1147,1696 @@ export class CoolifyService {
999
1147
  return ok(result.data as ICoolifyApplication);
1000
1148
  }
1001
1149
 
1150
+ // ===========================================================================
1151
+ // Version / Health
1152
+ // ===========================================================================
1153
+
1002
1154
  /**
1003
- * Lists all active and queued deployments.
1155
+ * Gets the Coolify server version.
1004
1156
  *
1005
- * @returns Result with deployments list or error
1157
+ * @returns Result with version info or error
1006
1158
  */
1007
- async listDeployments(): Promise<Result<ICoolifyDeployment[], Error>> {
1008
- log.info("Listing active deployments");
1159
+ async getVersion(): Promise<Result<ICoolifyVersion, Error>> {
1160
+ log.info("Getting Coolify version");
1009
1161
 
1010
- const result = await this.request<ICoolifyDeployment[]>("/deployments");
1162
+ // /version returns plain text, not JSON
1163
+ if (!this.baseUrl || !this.token) {
1164
+ return err(new Error("Coolify not configured"));
1165
+ }
1011
1166
 
1012
- if (result.error) {
1013
- log.error(`Failed to list deployments: ${result.error}`);
1014
- return err(new Error(result.error));
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));
1015
1188
  }
1189
+ }
1016
1190
 
1017
- log.success(`Listed ${result.data?.length || 0} active deployments`);
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`);
1018
1218
  return ok(result.data || []);
1019
1219
  }
1020
1220
 
1021
1221
  /**
1022
- * Gets detailed information about a specific deployment.
1222
+ * Gets details of a specific database.
1023
1223
  *
1024
- * @param deploymentUuid - Deployment UUID
1025
- * @returns Result with deployment details or error
1224
+ * @param uuid - Database UUID
1225
+ * @returns Result with database details or error
1026
1226
  */
1027
- async getDeployment(
1028
- deploymentUuid: string,
1029
- ): Promise<Result<ICoolifyDeployment, Error>> {
1030
- log.info(`Getting deployment details for ${deploymentUuid}`);
1227
+ async getDatabase(uuid: string): Promise<Result<ICoolifyDatabase, Error>> {
1228
+ log.info(`Getting database ${uuid}`);
1031
1229
 
1032
- const result = await this.request<ICoolifyDeployment>(
1033
- `/deployments/${deploymentUuid}`,
1034
- );
1230
+ const result = await this.request<ICoolifyDatabase>(`/databases/${uuid}`);
1231
+ if (result.error) return err(new Error(result.error));
1035
1232
 
1036
- if (result.error) {
1037
- log.error(`Failed to get deployment: ${result.error}`);
1038
- return err(new Error(result.error));
1039
- }
1233
+ return ok(result.data as ICoolifyDatabase);
1234
+ }
1040
1235
 
1041
- log.success(`Deployment details retrieved: ${deploymentUuid}`);
1042
- return ok(result.data as ICoolifyDeployment);
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!);
1043
1260
  }
1044
1261
 
1045
1262
  /**
1046
- * Gets logs for a specific deployment.
1263
+ * Updates a database configuration.
1047
1264
  *
1048
- * @param deploymentUuid - Deployment UUID
1049
- * @returns Result with deployment status and logs or error
1265
+ * @param uuid - Database UUID
1266
+ * @param data - Update data
1267
+ * @returns Result with updated database or error
1050
1268
  */
1051
- async getDeploymentLogs(
1052
- deploymentUuid: string,
1053
- ): Promise<
1054
- Result<{ status: string; logs: string; deployment_uuid: string }, Error>
1055
- > {
1056
- log.info(`Getting deployment logs for ${deploymentUuid}`);
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));
1057
1280
 
1058
- const result = await this.request<{
1059
- status: string;
1060
- logs: string;
1061
- deployment_uuid: string;
1062
- }>(`/deployments/${deploymentUuid}`);
1281
+ log.success(`Database updated: ${uuid}`);
1282
+ return ok(result.data as ICoolifyDatabase);
1283
+ }
1063
1284
 
1064
- if (result.error) {
1065
- log.error(`Failed to get deployment logs: ${result.error}`);
1066
- return err(new Error(result.error));
1067
- }
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}`);
1068
1302
 
1069
- log.success(`Deployment logs retrieved: ${deploymentUuid}`);
1070
- return ok({
1071
- status: result.data?.status || "unknown",
1072
- logs: result.data?.logs || "",
1073
- deployment_uuid: result.data?.deployment_uuid || deploymentUuid,
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",
1074
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" });
1075
1321
  }
1076
1322
 
1077
1323
  /**
1078
- * Gets deployment history for a specific application.
1324
+ * Starts a database.
1079
1325
  *
1080
- * @param appUuid - Application UUID
1081
- * @param skip - Number of deployments to skip
1082
- * @param take - Number of deployments to return
1083
- * @returns Result with deployments list or error
1326
+ * @param uuid - Database UUID
1327
+ * @returns Result indicating success or error
1084
1328
  */
1085
- async getApplicationDeployments(
1086
- appUuid: string,
1087
- skip: number = 0,
1088
- take: number = 10,
1089
- ): Promise<Result<ICoolifyDeployment[], Error>> {
1090
- log.info(`Getting deployments for application ${appUuid}`);
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));
1091
1339
 
1092
- const params = new URLSearchParams();
1093
- if (skip > 0) params.set("skip", skip.toString());
1094
- if (take !== 10) params.set("take", take.toString());
1340
+ log.success(`Database started: ${uuid}`);
1341
+ return ok(result.data || { message: "Database started" });
1342
+ }
1095
1343
 
1096
- const endpoint = `/applications/${appUuid}/deployments${params.toString() ? `?${params.toString()}` : ""}`;
1097
- const result = await this.request<{
1098
- count: number;
1099
- deployments: ICoolifyDeployment[];
1100
- }>(endpoint);
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));
1101
1360
 
1102
- if (result.error) {
1103
- log.error(`Failed to get application deployments: ${result.error}`);
1104
- return err(new Error(result.error));
1105
- }
1361
+ log.success(`Database stopped: ${uuid}`);
1362
+ return ok(result.data || { message: "Database stopped" });
1363
+ }
1106
1364
 
1107
- const deployments = result.data?.deployments || [];
1108
- log.success(`Retrieved ${deployments.length} deployments for ${appUuid}`);
1109
- return ok(deployments);
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" });
1110
1384
  }
1111
1385
 
1386
+ // ===========================================================================
1387
+ // Database Backup endpoints
1388
+ // ===========================================================================
1389
+
1112
1390
  /**
1113
- * Lists deployments for a specific application.
1391
+ * Lists backups for a database.
1114
1392
  *
1115
- * Uses the /deployments/applications/{appUuid} endpoint which returns
1116
- * all deployments (active, queued, and completed) for a single application.
1117
- * This differs from listDeployments() which returns ALL deployments globally.
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.
1118
1411
  *
1119
- * @param appUuid - Application UUID
1120
- * @returns Result with deployments list or error
1412
+ * @param databaseUuid - Database UUID
1413
+ * @param backupUuid - Backup UUID
1414
+ * @returns Result with backup details or error
1121
1415
  */
1122
- async listApplicationDeployments(
1123
- appUuid: string,
1124
- ): Promise<Result<ICoolifyDeployment[], Error>> {
1125
- log.info(`Listing deployments for application ${appUuid}`);
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));
1126
1424
 
1127
- // API returns { count: number, deployments: ICoolifyDeployment[] }
1128
- const result = await this.request<{
1129
- count: number;
1130
- deployments: ICoolifyDeployment[];
1131
- }>(`/deployments/applications/${appUuid}`);
1425
+ return ok(result.data as ICoolifyDatabaseBackup);
1426
+ }
1132
1427
 
1133
- if (result.error) {
1134
- log.error(`Failed to list application deployments: ${result.error}`);
1135
- return err(new Error(result.error));
1136
- }
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));
1137
1446
 
1138
- const deployments = result.data?.deployments || [];
1139
- log.success(`Listed ${deployments.length} deployments for ${appUuid}`);
1140
- return ok(deployments);
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" });
2138
+ }
2139
+
2140
+ /**
2141
+ * Lists all active and queued deployments.
2142
+ *
2143
+ * @param page - Optional page number for pagination
2144
+ * @param perPage - Optional number of items per page
2145
+ * @returns Result with deployments list or error
2146
+ */
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
+ }
2160
+
2161
+ const result = await this.request<ICoolifyDeployment[]>(endpoint);
2162
+
2163
+ if (result.error) {
2164
+ log.error(`Failed to list deployments: ${result.error}`);
2165
+ return err(new Error(result.error));
2166
+ }
2167
+
2168
+ log.success(`Listed ${result.data?.length || 0} active deployments`);
2169
+ return ok(result.data || []);
2170
+ }
2171
+
2172
+ /**
2173
+ * Gets detailed information about a specific deployment.
2174
+ *
2175
+ * @param deploymentUuid - Deployment UUID
2176
+ * @returns Result with deployment details or error
2177
+ */
2178
+ async getDeployment(
2179
+ deploymentUuid: string,
2180
+ ): Promise<Result<ICoolifyDeployment, Error>> {
2181
+ log.info(`Getting deployment details for ${deploymentUuid}`);
2182
+
2183
+ const result = await this.request<ICoolifyDeployment>(
2184
+ `/deployments/${deploymentUuid}`,
2185
+ );
2186
+
2187
+ if (result.error) {
2188
+ log.error(`Failed to get deployment: ${result.error}`);
2189
+ return err(new Error(result.error));
2190
+ }
2191
+
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
+ });
2226
+ }
2227
+
2228
+ /**
2229
+ * Gets deployment history for a specific application.
2230
+ *
2231
+ * @param appUuid - Application UUID
2232
+ * @param skip - Number of deployments to skip
2233
+ * @param take - Number of deployments to return
2234
+ * @returns Result with deployments list or error
2235
+ */
2236
+ async getApplicationDeployments(
2237
+ appUuid: string,
2238
+ skip: number = 0,
2239
+ take: number = 10,
2240
+ ): Promise<Result<ICoolifyDeployment[], Error>> {
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
+ }
2262
+
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}`);
2277
+
2278
+ // API returns { count: number, deployments: ICoolifyDeployment[] }
2279
+ const result = await this.request<{
2280
+ count: number;
2281
+ deployments: ICoolifyDeployment[];
2282
+ }>(`/deployments/applications/${appUuid}`);
2283
+
2284
+ if (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
+ }
2716
+ }
2717
+
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
+ );
1141
2840
  }
1142
2841
  }
1143
2842
 
@@ -1158,11 +2857,18 @@ export function getCoolifyService(): CoolifyService {
1158
2857
  // Re-export types
1159
2858
  export type {
1160
2859
  ICoolifyServer,
2860
+ ICoolifyServerResource,
2861
+ ICoolifyServerDomain,
1161
2862
  ICoolifyDestination,
1162
2863
  ICoolifyProject,
1163
2864
  ICoolifyTeam,
1164
2865
  ICoolifyApplication,
2866
+ ICoolifyDatabase,
2867
+ ICoolifyDatabaseBackup,
2868
+ ICoolifyService as ICoolifyServiceType,
2869
+ ICoolifyPrivateKey,
1165
2870
  ICoolifyDeployment,
2871
+ ICoolifyVersion,
1166
2872
  ICoolifyAppOptions,
1167
2873
  ICoolifyAppResult,
1168
2874
  ICoolifyDeployOptions,