@masonator/coolify-mcp 2.10.0 → 2.12.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.
@@ -149,6 +149,61 @@ describe('CoolifyMcpServer v2', () => {
149
149
  expect(typeof client.updateCloudToken).toBe('function');
150
150
  expect(typeof client.deleteCloudToken).toBe('function');
151
151
  expect(typeof client.validateCloudToken).toBe('function');
152
+ // Application storage operations
153
+ expect(typeof client.listApplicationStorages).toBe('function');
154
+ expect(typeof client.createApplicationStorage).toBe('function');
155
+ expect(typeof client.updateApplicationStorage).toBe('function');
156
+ expect(typeof client.deleteApplicationStorage).toBe('function');
157
+ // Application scheduled task operations
158
+ expect(typeof client.listApplicationScheduledTasks).toBe('function');
159
+ expect(typeof client.createApplicationScheduledTask).toBe('function');
160
+ expect(typeof client.updateApplicationScheduledTask).toBe('function');
161
+ expect(typeof client.deleteApplicationScheduledTask).toBe('function');
162
+ expect(typeof client.listApplicationScheduledTaskExecutions).toBe('function');
163
+ // Application preview operations
164
+ expect(typeof client.deleteApplicationPreview).toBe('function');
165
+ // Database environment variable operations
166
+ expect(typeof client.listDatabaseEnvVars).toBe('function');
167
+ expect(typeof client.createDatabaseEnvVar).toBe('function');
168
+ expect(typeof client.updateDatabaseEnvVar).toBe('function');
169
+ expect(typeof client.bulkUpdateDatabaseEnvVars).toBe('function');
170
+ expect(typeof client.deleteDatabaseEnvVar).toBe('function');
171
+ // Database storage operations
172
+ expect(typeof client.listDatabaseStorages).toBe('function');
173
+ expect(typeof client.createDatabaseStorage).toBe('function');
174
+ expect(typeof client.updateDatabaseStorage).toBe('function');
175
+ expect(typeof client.deleteDatabaseStorage).toBe('function');
176
+ // Delete backup execution
177
+ expect(typeof client.deleteBackupExecution).toBe('function');
178
+ // Service env var bulk operations
179
+ expect(typeof client.bulkUpdateServiceEnvVars).toBe('function');
180
+ // Service storage operations
181
+ expect(typeof client.listServiceStorages).toBe('function');
182
+ expect(typeof client.createServiceStorage).toBe('function');
183
+ expect(typeof client.updateServiceStorage).toBe('function');
184
+ expect(typeof client.deleteServiceStorage).toBe('function');
185
+ // Service scheduled task operations
186
+ expect(typeof client.listServiceScheduledTasks).toBe('function');
187
+ expect(typeof client.createServiceScheduledTask).toBe('function');
188
+ expect(typeof client.updateServiceScheduledTask).toBe('function');
189
+ expect(typeof client.deleteServiceScheduledTask).toBe('function');
190
+ expect(typeof client.listServiceScheduledTaskExecutions).toBe('function');
191
+ // Hetzner cloud operations
192
+ expect(typeof client.listHetznerLocations).toBe('function');
193
+ expect(typeof client.listHetznerServerTypes).toBe('function');
194
+ expect(typeof client.listHetznerImages).toBe('function');
195
+ expect(typeof client.listHetznerSSHKeys).toBe('function');
196
+ expect(typeof client.createHetznerServer).toBe('function');
197
+ // GitHub App repository operations
198
+ expect(typeof client.listGitHubAppRepositories).toBe('function');
199
+ expect(typeof client.listGitHubAppBranches).toBe('function');
200
+ // Resources operations
201
+ expect(typeof client.listResources).toBe('function');
202
+ // Health operations
203
+ expect(typeof client.getHealth).toBe('function');
204
+ // API enable/disable operations
205
+ expect(typeof client.enableApi).toBe('function');
206
+ expect(typeof client.disableApi).toBe('function');
152
207
  // Version caching
153
208
  expect(typeof client.getCachedVersion).toBe('function');
154
209
  });
@@ -245,6 +300,22 @@ describe('CoolifyMcpServer v2', () => {
245
300
  expect(result.content[0].text).toContain('key, value required');
246
301
  });
247
302
  });
303
+ describe('system tool handler', () => {
304
+ const callSystem = async (srv, args) => {
305
+ const tool = srv._registeredTools['system'];
306
+ return tool.handler(args, {});
307
+ };
308
+ it('forwards include_full and reveal to listResources', async () => {
309
+ const spy = jest.spyOn(server['client'], 'listResources').mockResolvedValue([]);
310
+ await callSystem(server, { action: 'list_resources', include_full: true, reveal: true });
311
+ expect(spy).toHaveBeenCalledWith({ include_full: true, reveal: true });
312
+ });
313
+ it('calls listResources with undefined flags when neither is passed', async () => {
314
+ const spy = jest.spyOn(server['client'], 'listResources').mockResolvedValue([]);
315
+ await callSystem(server, { action: 'list_resources' });
316
+ expect(spy).toHaveBeenCalledWith({ include_full: undefined, reveal: undefined });
317
+ });
318
+ });
248
319
  describe('bulk_env_update tool handler', () => {
249
320
  it('forwards is_buildtime/is_runtime to bulkEnvUpdate', async () => {
250
321
  const spy = jest.spyOn(server['client'], 'bulkEnvUpdate').mockResolvedValue({
@@ -2,7 +2,7 @@
2
2
  * Coolify API Client
3
3
  * Complete HTTP client for the Coolify API v1
4
4
  */
5
- import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, CreatePostgresqlRequest, CreateMysqlRequest, CreateMariadbRequest, CreateMongodbRequest, CreateRedisRequest, CreateKeydbRequest, CreateClickhouseRequest, CreateDragonflyRequest, CreateDatabaseResponse, DatabaseBackup, BackupExecution, CreateDatabaseBackupRequest, UpdateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, DeploymentEssential, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, GitHubApp, CreateGitHubAppRequest, UpdateGitHubAppRequest, GitHubAppUpdateResponse, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult } from '../types/coolify.js';
5
+ import type { CoolifyConfig, DeleteOptions, MessageResponse, UuidResponse, Server, ServerResource, ServerDomain, ServerValidation, CreateServerRequest, UpdateServerRequest, Project, CreateProjectRequest, UpdateProjectRequest, Environment, CreateEnvironmentRequest, Application, CreateApplicationPublicRequest, CreateApplicationPrivateGHRequest, CreateApplicationPrivateKeyRequest, CreateApplicationDockerfileRequest, CreateApplicationDockerImageRequest, CreateApplicationDockerComposeRequest, UpdateApplicationRequest, ApplicationActionResponse, EnvironmentVariable, EnvVarSummary, CreateEnvVarRequest, UpdateEnvVarRequest, BulkUpdateEnvVarsRequest, Database, UpdateDatabaseRequest, CreatePostgresqlRequest, CreateMysqlRequest, CreateMariadbRequest, CreateMongodbRequest, CreateRedisRequest, CreateKeydbRequest, CreateClickhouseRequest, CreateDragonflyRequest, CreateDatabaseResponse, DatabaseBackup, BackupExecution, CreateDatabaseBackupRequest, UpdateDatabaseBackupRequest, Service, CreateServiceRequest, UpdateServiceRequest, ServiceCreateResponse, Deployment, DeploymentEssential, Team, TeamMember, PrivateKey, CreatePrivateKeyRequest, UpdatePrivateKeyRequest, GitHubApp, CreateGitHubAppRequest, UpdateGitHubAppRequest, GitHubAppUpdateResponse, CloudToken, CreateCloudTokenRequest, UpdateCloudTokenRequest, CloudTokenValidation, Version, StorageListResponse, CreateStorageRequest, UpdateStorageRequest, ScheduledTask, ScheduledTaskExecution, CreateScheduledTaskRequest, UpdateScheduledTaskRequest, HetznerLocation, HetznerServerType, HetznerImage, HetznerSSHKey, CreateHetznerServerRequest, CreateHetznerServerResponse, GitHubRepository, GitHubBranch, ApplicationDiagnostic, ServerDiagnostic, InfrastructureIssuesReport, BatchOperationResult, ResourceListItem, ResourceListItemFull } from '../types/coolify.js';
6
6
  export interface ListOptions {
7
7
  page?: number;
8
8
  per_page?: number;
@@ -225,6 +225,65 @@ export declare class CoolifyClient {
225
225
  createDatabaseBackup(databaseUuid: string, data: CreateDatabaseBackupRequest): Promise<DatabaseBackup>;
226
226
  updateDatabaseBackup(databaseUuid: string, backupUuid: string, data: UpdateDatabaseBackupRequest): Promise<MessageResponse>;
227
227
  deleteDatabaseBackup(databaseUuid: string, backupUuid: string): Promise<MessageResponse>;
228
+ listApplicationStorages(uuid: string): Promise<StorageListResponse>;
229
+ createApplicationStorage(uuid: string, data: CreateStorageRequest): Promise<MessageResponse>;
230
+ updateApplicationStorage(uuid: string, data: UpdateStorageRequest): Promise<MessageResponse>;
231
+ deleteApplicationStorage(uuid: string, storageUuid: string): Promise<MessageResponse>;
232
+ listApplicationScheduledTasks(uuid: string): Promise<ScheduledTask[]>;
233
+ createApplicationScheduledTask(uuid: string, data: CreateScheduledTaskRequest): Promise<ScheduledTask>;
234
+ updateApplicationScheduledTask(uuid: string, taskUuid: string, data: UpdateScheduledTaskRequest): Promise<ScheduledTask>;
235
+ deleteApplicationScheduledTask(uuid: string, taskUuid: string): Promise<MessageResponse>;
236
+ listApplicationScheduledTaskExecutions(uuid: string, taskUuid: string): Promise<ScheduledTaskExecution[]>;
237
+ deleteApplicationPreview(uuid: string, pullRequestId: number): Promise<MessageResponse>;
238
+ listDatabaseEnvVars(uuid: string): Promise<EnvironmentVariable[]>;
239
+ createDatabaseEnvVar(uuid: string, data: CreateEnvVarRequest): Promise<UuidResponse>;
240
+ updateDatabaseEnvVar(uuid: string, data: UpdateEnvVarRequest): Promise<MessageResponse>;
241
+ bulkUpdateDatabaseEnvVars(uuid: string, data: BulkUpdateEnvVarsRequest): Promise<MessageResponse>;
242
+ deleteDatabaseEnvVar(uuid: string, envUuid: string): Promise<MessageResponse>;
243
+ listDatabaseStorages(uuid: string): Promise<StorageListResponse>;
244
+ createDatabaseStorage(uuid: string, data: CreateStorageRequest): Promise<MessageResponse>;
245
+ updateDatabaseStorage(uuid: string, data: UpdateStorageRequest): Promise<MessageResponse>;
246
+ deleteDatabaseStorage(uuid: string, storageUuid: string): Promise<MessageResponse>;
247
+ deleteBackupExecution(databaseUuid: string, backupUuid: string, executionUuid: string): Promise<MessageResponse>;
248
+ bulkUpdateServiceEnvVars(uuid: string, data: BulkUpdateEnvVarsRequest): Promise<MessageResponse>;
249
+ listServiceStorages(uuid: string): Promise<StorageListResponse>;
250
+ createServiceStorage(uuid: string, data: CreateStorageRequest): Promise<MessageResponse>;
251
+ updateServiceStorage(uuid: string, data: UpdateStorageRequest): Promise<MessageResponse>;
252
+ deleteServiceStorage(uuid: string, storageUuid: string): Promise<MessageResponse>;
253
+ listServiceScheduledTasks(uuid: string): Promise<ScheduledTask[]>;
254
+ createServiceScheduledTask(uuid: string, data: CreateScheduledTaskRequest): Promise<ScheduledTask>;
255
+ updateServiceScheduledTask(uuid: string, taskUuid: string, data: UpdateScheduledTaskRequest): Promise<ScheduledTask>;
256
+ deleteServiceScheduledTask(uuid: string, taskUuid: string): Promise<MessageResponse>;
257
+ listServiceScheduledTaskExecutions(uuid: string, taskUuid: string): Promise<ScheduledTaskExecution[]>;
258
+ listHetznerLocations(tokenUuid: string): Promise<HetznerLocation[]>;
259
+ listHetznerServerTypes(tokenUuid: string): Promise<HetznerServerType[]>;
260
+ listHetznerImages(tokenUuid: string): Promise<HetznerImage[]>;
261
+ listHetznerSSHKeys(tokenUuid: string): Promise<HetznerSSHKey[]>;
262
+ createHetznerServer(data: CreateHetznerServerRequest): Promise<CreateHetznerServerResponse>;
263
+ listGitHubAppRepositories(githubAppId: number): Promise<GitHubRepository[]>;
264
+ listGitHubAppBranches(githubAppId: number, owner: string, repo: string): Promise<GitHubBranch[]>;
265
+ /**
266
+ * List every resource on the Coolify instance.
267
+ *
268
+ * Defaults to an essential projection ({@link ResourceListItem}: uuid, name,
269
+ * type, optional status) — Coolify's `/api/v1/resources` endpoint actually
270
+ * returns ~95 fields per row including the full build/healthcheck/limits
271
+ * config, which on a moderate instance can exceed 500 KB on a single call
272
+ * and blow MCP/LLM context budgets. Set `include_full: true` to opt back
273
+ * into the raw response shape ({@link ResourceListItemFull}).
274
+ *
275
+ * When `include_full: true`, sensitive fields ({@link SENSITIVE_RESOURCE_FIELDS}:
276
+ * webhook HMAC secrets + basic-auth password) are replaced with `'***'`
277
+ * unless the caller also passes `reveal: true`. Mirrors the v2.9.0 env_vars
278
+ * masking posture.
279
+ */
280
+ listResources(options?: {
281
+ include_full?: boolean;
282
+ reveal?: boolean;
283
+ }): Promise<ResourceListItem[] | ResourceListItemFull[]>;
284
+ getHealth(): Promise<MessageResponse>;
285
+ enableApi(): Promise<MessageResponse>;
286
+ disableApi(): Promise<MessageResponse>;
228
287
  cancelDeployment(uuid: string): Promise<MessageResponse>;
229
288
  /**
230
289
  * Check if a string looks like a UUID (Coolify format or standard format).
@@ -180,6 +180,58 @@ function maskEnvVarSummary(envVar) {
180
180
  value: MASKED_VALUE,
181
181
  };
182
182
  }
183
+ /**
184
+ * Project a full Coolify resource row down to {@link ResourceListItem} — the
185
+ * four fields callers actually need for enumeration (uuid, name, type, status).
186
+ * Drops the ~90 extra fields Coolify returns by default to keep MCP token
187
+ * budgets sane.
188
+ */
189
+ function toResourceListItemEssential(item) {
190
+ const essential = {
191
+ uuid: item.uuid,
192
+ name: item.name,
193
+ type: item.type,
194
+ };
195
+ if (typeof item.status === 'string') {
196
+ essential.status = item.status;
197
+ }
198
+ return essential;
199
+ }
200
+ /**
201
+ * Per-resource sensitive fields returned by Coolify's `/api/v1/resources`
202
+ * endpoint that are masked by default in {@link CoolifyClient.listResources}
203
+ * when `include_full: true` is passed. Mirrors the v2.9.0 env-var masking
204
+ * posture: the underlying API exposes these via the same access token, but
205
+ * the MCP layer narrows the trust boundary so an LLM client that was granted
206
+ * "list resources" doesn't silently exfiltrate webhook HMAC secrets or
207
+ * basic-auth credentials.
208
+ *
209
+ * The `manual_webhook_secret_*` fields are HMAC signing keys for inbound
210
+ * deploy webhooks — anyone with one can forge deploys for that repo
211
+ * independently of the Coolify API token. `http_basic_auth_password` is the
212
+ * password gating front-of-app access.
213
+ */
214
+ const SENSITIVE_RESOURCE_FIELDS = [
215
+ 'manual_webhook_secret_github',
216
+ 'manual_webhook_secret_gitlab',
217
+ 'manual_webhook_secret_gitea',
218
+ 'manual_webhook_secret_bitbucket',
219
+ 'http_basic_auth_password',
220
+ ];
221
+ /**
222
+ * Replace each {@link SENSITIVE_RESOURCE_FIELDS} entry with `'***'` on a full
223
+ * resource row. Null/undefined values are preserved (since `null` conveys
224
+ * "no secret set" and matters to callers); only populated values get masked.
225
+ */
226
+ function maskResourceItemFull(item) {
227
+ const masked = { ...item };
228
+ for (const field of SENSITIVE_RESOURCE_FIELDS) {
229
+ if (masked[field] != null) {
230
+ masked[field] = MASKED_VALUE;
231
+ }
232
+ }
233
+ return masked;
234
+ }
183
235
  /**
184
236
  * HTTP client for the Coolify API
185
237
  */
@@ -922,6 +974,250 @@ export class CoolifyClient {
922
974
  });
923
975
  }
924
976
  // ===========================================================================
977
+ // Application Storage endpoints
978
+ // ===========================================================================
979
+ async listApplicationStorages(uuid) {
980
+ return this.request(`/applications/${uuid}/storages`);
981
+ }
982
+ async createApplicationStorage(uuid, data) {
983
+ return this.request(`/applications/${uuid}/storages`, {
984
+ method: 'POST',
985
+ body: JSON.stringify(data),
986
+ });
987
+ }
988
+ async updateApplicationStorage(uuid, data) {
989
+ return this.request(`/applications/${uuid}/storages`, {
990
+ method: 'PATCH',
991
+ body: JSON.stringify(data),
992
+ });
993
+ }
994
+ async deleteApplicationStorage(uuid, storageUuid) {
995
+ return this.request(`/applications/${uuid}/storages/${storageUuid}`, {
996
+ method: 'DELETE',
997
+ });
998
+ }
999
+ // ===========================================================================
1000
+ // Application Scheduled Task endpoints
1001
+ // ===========================================================================
1002
+ async listApplicationScheduledTasks(uuid) {
1003
+ return this.request(`/applications/${uuid}/scheduled-tasks`);
1004
+ }
1005
+ async createApplicationScheduledTask(uuid, data) {
1006
+ return this.request(`/applications/${uuid}/scheduled-tasks`, {
1007
+ method: 'POST',
1008
+ body: JSON.stringify(data),
1009
+ });
1010
+ }
1011
+ async updateApplicationScheduledTask(uuid, taskUuid, data) {
1012
+ return this.request(`/applications/${uuid}/scheduled-tasks/${taskUuid}`, {
1013
+ method: 'PATCH',
1014
+ body: JSON.stringify(data),
1015
+ });
1016
+ }
1017
+ async deleteApplicationScheduledTask(uuid, taskUuid) {
1018
+ return this.request(`/applications/${uuid}/scheduled-tasks/${taskUuid}`, {
1019
+ method: 'DELETE',
1020
+ });
1021
+ }
1022
+ async listApplicationScheduledTaskExecutions(uuid, taskUuid) {
1023
+ return this.request(`/applications/${uuid}/scheduled-tasks/${taskUuid}/executions`);
1024
+ }
1025
+ // ===========================================================================
1026
+ // Application Preview endpoints
1027
+ // ===========================================================================
1028
+ async deleteApplicationPreview(uuid, pullRequestId) {
1029
+ return this.request(`/applications/${uuid}/previews/${pullRequestId}`, {
1030
+ method: 'DELETE',
1031
+ });
1032
+ }
1033
+ // ===========================================================================
1034
+ // Database Environment Variable endpoints
1035
+ // ===========================================================================
1036
+ async listDatabaseEnvVars(uuid) {
1037
+ return this.request(`/databases/${uuid}/envs`);
1038
+ }
1039
+ async createDatabaseEnvVar(uuid, data) {
1040
+ return this.request(`/databases/${uuid}/envs`, {
1041
+ method: 'POST',
1042
+ body: JSON.stringify(cleanRequestData(data)),
1043
+ });
1044
+ }
1045
+ async updateDatabaseEnvVar(uuid, data) {
1046
+ return this.request(`/databases/${uuid}/envs`, {
1047
+ method: 'PATCH',
1048
+ body: JSON.stringify(cleanRequestData(data)),
1049
+ });
1050
+ }
1051
+ async bulkUpdateDatabaseEnvVars(uuid, data) {
1052
+ return this.request(`/databases/${uuid}/envs/bulk`, {
1053
+ method: 'PATCH',
1054
+ body: JSON.stringify(data),
1055
+ });
1056
+ }
1057
+ async deleteDatabaseEnvVar(uuid, envUuid) {
1058
+ return this.request(`/databases/${uuid}/envs/${envUuid}`, {
1059
+ method: 'DELETE',
1060
+ });
1061
+ }
1062
+ // ===========================================================================
1063
+ // Database Storage endpoints
1064
+ // ===========================================================================
1065
+ async listDatabaseStorages(uuid) {
1066
+ return this.request(`/databases/${uuid}/storages`);
1067
+ }
1068
+ async createDatabaseStorage(uuid, data) {
1069
+ return this.request(`/databases/${uuid}/storages`, {
1070
+ method: 'POST',
1071
+ body: JSON.stringify(data),
1072
+ });
1073
+ }
1074
+ async updateDatabaseStorage(uuid, data) {
1075
+ return this.request(`/databases/${uuid}/storages`, {
1076
+ method: 'PATCH',
1077
+ body: JSON.stringify(data),
1078
+ });
1079
+ }
1080
+ async deleteDatabaseStorage(uuid, storageUuid) {
1081
+ return this.request(`/databases/${uuid}/storages/${storageUuid}`, {
1082
+ method: 'DELETE',
1083
+ });
1084
+ }
1085
+ // ===========================================================================
1086
+ // Delete Backup Execution endpoint
1087
+ // ===========================================================================
1088
+ async deleteBackupExecution(databaseUuid, backupUuid, executionUuid) {
1089
+ return this.request(`/databases/${databaseUuid}/backups/${backupUuid}/executions/${executionUuid}`, { method: 'DELETE' });
1090
+ }
1091
+ // ===========================================================================
1092
+ // Service Environment Variable (bulk) endpoint
1093
+ // ===========================================================================
1094
+ async bulkUpdateServiceEnvVars(uuid, data) {
1095
+ return this.request(`/services/${uuid}/envs/bulk`, {
1096
+ method: 'PATCH',
1097
+ body: JSON.stringify(data),
1098
+ });
1099
+ }
1100
+ // ===========================================================================
1101
+ // Service Storage endpoints
1102
+ // ===========================================================================
1103
+ async listServiceStorages(uuid) {
1104
+ return this.request(`/services/${uuid}/storages`);
1105
+ }
1106
+ async createServiceStorage(uuid, data) {
1107
+ return this.request(`/services/${uuid}/storages`, {
1108
+ method: 'POST',
1109
+ body: JSON.stringify(data),
1110
+ });
1111
+ }
1112
+ async updateServiceStorage(uuid, data) {
1113
+ return this.request(`/services/${uuid}/storages`, {
1114
+ method: 'PATCH',
1115
+ body: JSON.stringify(data),
1116
+ });
1117
+ }
1118
+ async deleteServiceStorage(uuid, storageUuid) {
1119
+ return this.request(`/services/${uuid}/storages/${storageUuid}`, {
1120
+ method: 'DELETE',
1121
+ });
1122
+ }
1123
+ // ===========================================================================
1124
+ // Service Scheduled Task endpoints
1125
+ // ===========================================================================
1126
+ async listServiceScheduledTasks(uuid) {
1127
+ return this.request(`/services/${uuid}/scheduled-tasks`);
1128
+ }
1129
+ async createServiceScheduledTask(uuid, data) {
1130
+ return this.request(`/services/${uuid}/scheduled-tasks`, {
1131
+ method: 'POST',
1132
+ body: JSON.stringify(data),
1133
+ });
1134
+ }
1135
+ async updateServiceScheduledTask(uuid, taskUuid, data) {
1136
+ return this.request(`/services/${uuid}/scheduled-tasks/${taskUuid}`, {
1137
+ method: 'PATCH',
1138
+ body: JSON.stringify(data),
1139
+ });
1140
+ }
1141
+ async deleteServiceScheduledTask(uuid, taskUuid) {
1142
+ return this.request(`/services/${uuid}/scheduled-tasks/${taskUuid}`, {
1143
+ method: 'DELETE',
1144
+ });
1145
+ }
1146
+ async listServiceScheduledTaskExecutions(uuid, taskUuid) {
1147
+ return this.request(`/services/${uuid}/scheduled-tasks/${taskUuid}/executions`);
1148
+ }
1149
+ // ===========================================================================
1150
+ // Hetzner Cloud endpoints
1151
+ // ===========================================================================
1152
+ async listHetznerLocations(tokenUuid) {
1153
+ return this.request(`/hetzner/locations?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1154
+ }
1155
+ async listHetznerServerTypes(tokenUuid) {
1156
+ return this.request(`/hetzner/server-types?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1157
+ }
1158
+ async listHetznerImages(tokenUuid) {
1159
+ return this.request(`/hetzner/images?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1160
+ }
1161
+ async listHetznerSSHKeys(tokenUuid) {
1162
+ return this.request(`/hetzner/ssh-keys?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1163
+ }
1164
+ async createHetznerServer(data) {
1165
+ return this.request('/servers/hetzner', {
1166
+ method: 'POST',
1167
+ body: JSON.stringify(data),
1168
+ });
1169
+ }
1170
+ // ===========================================================================
1171
+ // GitHub App Repository endpoints
1172
+ // ===========================================================================
1173
+ async listGitHubAppRepositories(githubAppId) {
1174
+ const response = await this.request(`/github-apps/${githubAppId}/repositories`);
1175
+ return response.repositories ?? [];
1176
+ }
1177
+ async listGitHubAppBranches(githubAppId, owner, repo) {
1178
+ return this.request(`/github-apps/${githubAppId}/repositories/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`);
1179
+ }
1180
+ // ===========================================================================
1181
+ // Resources endpoint
1182
+ // ===========================================================================
1183
+ /**
1184
+ * List every resource on the Coolify instance.
1185
+ *
1186
+ * Defaults to an essential projection ({@link ResourceListItem}: uuid, name,
1187
+ * type, optional status) — Coolify's `/api/v1/resources` endpoint actually
1188
+ * returns ~95 fields per row including the full build/healthcheck/limits
1189
+ * config, which on a moderate instance can exceed 500 KB on a single call
1190
+ * and blow MCP/LLM context budgets. Set `include_full: true` to opt back
1191
+ * into the raw response shape ({@link ResourceListItemFull}).
1192
+ *
1193
+ * When `include_full: true`, sensitive fields ({@link SENSITIVE_RESOURCE_FIELDS}:
1194
+ * webhook HMAC secrets + basic-auth password) are replaced with `'***'`
1195
+ * unless the caller also passes `reveal: true`. Mirrors the v2.9.0 env_vars
1196
+ * masking posture.
1197
+ */
1198
+ async listResources(options) {
1199
+ const full = await this.request('/resources');
1200
+ if (options?.include_full !== true) {
1201
+ return full.map(toResourceListItemEssential);
1202
+ }
1203
+ return options.reveal === true ? full : full.map(maskResourceItemFull);
1204
+ }
1205
+ // ===========================================================================
1206
+ // Health endpoint
1207
+ // ===========================================================================
1208
+ async getHealth() {
1209
+ return this.request('/health');
1210
+ }
1211
+ // ===========================================================================
1212
+ // API Enable/Disable endpoints
1213
+ // ===========================================================================
1214
+ async enableApi() {
1215
+ return this.request('/enable', { method: 'GET' });
1216
+ }
1217
+ async disableApi() {
1218
+ return this.request('/disable', { method: 'GET' });
1219
+ }
1220
+ // ===========================================================================
925
1221
  // Deployment Control endpoints
926
1222
  // ===========================================================================
927
1223
  async cancelDeployment(uuid) {