@masonator/coolify-mcp 2.9.0 → 2.11.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
  });
@@ -263,6 +318,166 @@ describe('CoolifyMcpServer v2', () => {
263
318
  expect(spy).toHaveBeenCalledWith(['app-1', 'app-2'], 'PEM_KEY', 'multiline', false, true);
264
319
  });
265
320
  });
321
+ describe('application tool handler', () => {
322
+ // Regression for #178 — verify the application tool's create_* hand-picks
323
+ // forward build-config and health_check_* fields to the client. Previously
324
+ // these fields were accepted by zod but silently dropped by the hand-pick.
325
+ const callApplication = async (srv, args) => {
326
+ const tool = srv._registeredTools['application'];
327
+ return tool.handler(args, {});
328
+ };
329
+ const baseCreatePublic = {
330
+ action: 'create_public',
331
+ project_uuid: 'proj-uuid',
332
+ server_uuid: 'server-uuid',
333
+ git_repository: 'https://github.com/org/monorepo',
334
+ git_branch: 'main',
335
+ build_pack: 'dockerfile',
336
+ ports_exposes: '3000',
337
+ };
338
+ it('forwards build-config and health_check fields in create_public', async () => {
339
+ const spy = jest
340
+ .spyOn(server['client'], 'createApplicationPublic')
341
+ .mockResolvedValue({ uuid: 'app-1' });
342
+ await callApplication(server, {
343
+ ...baseCreatePublic,
344
+ base_directory: '/apps/api',
345
+ publish_directory: '/dist',
346
+ install_command: 'pnpm install',
347
+ build_command: 'pnpm build',
348
+ start_command: 'node dist/main.js',
349
+ dockerfile_location: '/apps/api/Dockerfile',
350
+ watch_paths: 'apps/api/**',
351
+ health_check_enabled: true,
352
+ health_check_path: '/health',
353
+ health_check_port: 3000,
354
+ health_check_start_period: 60,
355
+ });
356
+ expect(spy).toHaveBeenCalledWith(expect.objectContaining({
357
+ base_directory: '/apps/api',
358
+ publish_directory: '/dist',
359
+ install_command: 'pnpm install',
360
+ build_command: 'pnpm build',
361
+ start_command: 'node dist/main.js',
362
+ dockerfile_location: '/apps/api/Dockerfile',
363
+ watch_paths: 'apps/api/**',
364
+ health_check_enabled: true,
365
+ health_check_path: '/health',
366
+ health_check_port: 3000,
367
+ health_check_start_period: 60,
368
+ }));
369
+ });
370
+ it('forwards build-config and health_check fields in create_github', async () => {
371
+ const spy = jest
372
+ .spyOn(server['client'], 'createApplicationPrivateGH')
373
+ .mockResolvedValue({ uuid: 'app-2' });
374
+ await callApplication(server, {
375
+ action: 'create_github',
376
+ project_uuid: 'proj-uuid',
377
+ server_uuid: 'server-uuid',
378
+ github_app_uuid: 'gh-app-uuid',
379
+ git_repository: 'org/monorepo',
380
+ git_branch: 'main',
381
+ base_directory: '/apps/api',
382
+ dockerfile_location: '/apps/api/Dockerfile',
383
+ watch_paths: 'apps/api/**',
384
+ health_check_enabled: true,
385
+ health_check_path: '/health',
386
+ });
387
+ expect(spy).toHaveBeenCalledWith(expect.objectContaining({
388
+ base_directory: '/apps/api',
389
+ dockerfile_location: '/apps/api/Dockerfile',
390
+ watch_paths: 'apps/api/**',
391
+ health_check_enabled: true,
392
+ health_check_path: '/health',
393
+ }));
394
+ });
395
+ it('forwards build-config and health_check fields in create_key', async () => {
396
+ const spy = jest
397
+ .spyOn(server['client'], 'createApplicationPrivateKey')
398
+ .mockResolvedValue({ uuid: 'app-3' });
399
+ await callApplication(server, {
400
+ action: 'create_key',
401
+ project_uuid: 'proj-uuid',
402
+ server_uuid: 'server-uuid',
403
+ private_key_uuid: 'key-uuid',
404
+ git_repository: 'git@github.com:org/monorepo.git',
405
+ git_branch: 'main',
406
+ base_directory: '/apps/api',
407
+ publish_directory: '/dist',
408
+ install_command: 'pnpm install',
409
+ build_command: 'pnpm build',
410
+ start_command: 'node dist/main.js',
411
+ dockerfile_location: '/apps/api/Dockerfile',
412
+ watch_paths: 'apps/api/**',
413
+ health_check_enabled: true,
414
+ health_check_path: '/health',
415
+ health_check_port: 3000,
416
+ });
417
+ expect(spy).toHaveBeenCalledWith(expect.objectContaining({
418
+ base_directory: '/apps/api',
419
+ publish_directory: '/dist',
420
+ install_command: 'pnpm install',
421
+ build_command: 'pnpm build',
422
+ start_command: 'node dist/main.js',
423
+ dockerfile_location: '/apps/api/Dockerfile',
424
+ watch_paths: 'apps/api/**',
425
+ health_check_enabled: true,
426
+ health_check_path: '/health',
427
+ health_check_port: 3000,
428
+ }));
429
+ });
430
+ it('forwards health_check fields in create_dockerimage (build-config intentionally dropped)', async () => {
431
+ const spy = jest
432
+ .spyOn(server['client'], 'createApplicationDockerImage')
433
+ .mockResolvedValue({ uuid: 'app-4' });
434
+ // Caller passes both healthcheck AND build-config. Coolify's /applications/dockerimage
435
+ // endpoint doesn't accept build-config (pre-built image), so handler must drop those.
436
+ await callApplication(server, {
437
+ action: 'create_dockerimage',
438
+ project_uuid: 'proj-uuid',
439
+ server_uuid: 'server-uuid',
440
+ docker_registry_image_name: 'traefik/whoami',
441
+ ports_exposes: '80',
442
+ // Should be forwarded:
443
+ health_check_enabled: true,
444
+ health_check_path: '/health',
445
+ health_check_port: 80,
446
+ // Should NOT be forwarded (build-config not applicable to prebuilt image):
447
+ base_directory: '/should-be-dropped',
448
+ install_command: 'should-be-dropped',
449
+ dockerfile_location: '/should-be-dropped',
450
+ });
451
+ const forwarded = spy.mock.calls[0]?.[0];
452
+ expect(forwarded).toEqual(expect.objectContaining({
453
+ health_check_enabled: true,
454
+ health_check_path: '/health',
455
+ health_check_port: 80,
456
+ }));
457
+ expect(forwarded).not.toHaveProperty('base_directory');
458
+ expect(forwarded).not.toHaveProperty('install_command');
459
+ expect(forwarded).not.toHaveProperty('dockerfile_location');
460
+ });
461
+ it('forwards dockerfile_target_build through update (PATCH-only)', async () => {
462
+ const spy = jest.spyOn(server['client'], 'updateApplication').mockResolvedValue({});
463
+ await callApplication(server, {
464
+ action: 'update',
465
+ uuid: 'app-uuid',
466
+ dockerfile_location: '/apps/api/Dockerfile',
467
+ dockerfile_target_build: 'production',
468
+ base_directory: '/apps/api',
469
+ });
470
+ expect(spy).toHaveBeenCalledWith('app-uuid', expect.objectContaining({
471
+ dockerfile_location: '/apps/api/Dockerfile',
472
+ dockerfile_target_build: 'production',
473
+ base_directory: '/apps/api',
474
+ }));
475
+ // Confirm the update spread strips routing fields.
476
+ const updateData = spy.mock.calls[0]?.[1];
477
+ expect(updateData).not.toHaveProperty('action');
478
+ expect(updateData).not.toHaveProperty('uuid');
479
+ });
480
+ });
266
481
  });
267
482
  describe('truncateLogs', () => {
268
483
  // Plain text log tests
@@ -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 } from '../types/coolify.js';
6
6
  export interface ListOptions {
7
7
  page?: number;
8
8
  per_page?: number;
@@ -225,6 +225,47 @@ 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
+ listResources(): Promise<ResourceListItem[]>;
266
+ getHealth(): Promise<MessageResponse>;
267
+ enableApi(): Promise<MessageResponse>;
268
+ disableApi(): Promise<MessageResponse>;
228
269
  cancelDeployment(uuid: string): Promise<MessageResponse>;
229
270
  /**
230
271
  * Check if a string looks like a UUID (Coolify format or standard format).
@@ -922,6 +922,231 @@ export class CoolifyClient {
922
922
  });
923
923
  }
924
924
  // ===========================================================================
925
+ // Application Storage endpoints
926
+ // ===========================================================================
927
+ async listApplicationStorages(uuid) {
928
+ return this.request(`/applications/${uuid}/storages`);
929
+ }
930
+ async createApplicationStorage(uuid, data) {
931
+ return this.request(`/applications/${uuid}/storages`, {
932
+ method: 'POST',
933
+ body: JSON.stringify(data),
934
+ });
935
+ }
936
+ async updateApplicationStorage(uuid, data) {
937
+ return this.request(`/applications/${uuid}/storages`, {
938
+ method: 'PATCH',
939
+ body: JSON.stringify(data),
940
+ });
941
+ }
942
+ async deleteApplicationStorage(uuid, storageUuid) {
943
+ return this.request(`/applications/${uuid}/storages/${storageUuid}`, {
944
+ method: 'DELETE',
945
+ });
946
+ }
947
+ // ===========================================================================
948
+ // Application Scheduled Task endpoints
949
+ // ===========================================================================
950
+ async listApplicationScheduledTasks(uuid) {
951
+ return this.request(`/applications/${uuid}/scheduled-tasks`);
952
+ }
953
+ async createApplicationScheduledTask(uuid, data) {
954
+ return this.request(`/applications/${uuid}/scheduled-tasks`, {
955
+ method: 'POST',
956
+ body: JSON.stringify(data),
957
+ });
958
+ }
959
+ async updateApplicationScheduledTask(uuid, taskUuid, data) {
960
+ return this.request(`/applications/${uuid}/scheduled-tasks/${taskUuid}`, {
961
+ method: 'PATCH',
962
+ body: JSON.stringify(data),
963
+ });
964
+ }
965
+ async deleteApplicationScheduledTask(uuid, taskUuid) {
966
+ return this.request(`/applications/${uuid}/scheduled-tasks/${taskUuid}`, {
967
+ method: 'DELETE',
968
+ });
969
+ }
970
+ async listApplicationScheduledTaskExecutions(uuid, taskUuid) {
971
+ return this.request(`/applications/${uuid}/scheduled-tasks/${taskUuid}/executions`);
972
+ }
973
+ // ===========================================================================
974
+ // Application Preview endpoints
975
+ // ===========================================================================
976
+ async deleteApplicationPreview(uuid, pullRequestId) {
977
+ return this.request(`/applications/${uuid}/previews/${pullRequestId}`, {
978
+ method: 'DELETE',
979
+ });
980
+ }
981
+ // ===========================================================================
982
+ // Database Environment Variable endpoints
983
+ // ===========================================================================
984
+ async listDatabaseEnvVars(uuid) {
985
+ return this.request(`/databases/${uuid}/envs`);
986
+ }
987
+ async createDatabaseEnvVar(uuid, data) {
988
+ return this.request(`/databases/${uuid}/envs`, {
989
+ method: 'POST',
990
+ body: JSON.stringify(cleanRequestData(data)),
991
+ });
992
+ }
993
+ async updateDatabaseEnvVar(uuid, data) {
994
+ return this.request(`/databases/${uuid}/envs`, {
995
+ method: 'PATCH',
996
+ body: JSON.stringify(cleanRequestData(data)),
997
+ });
998
+ }
999
+ async bulkUpdateDatabaseEnvVars(uuid, data) {
1000
+ return this.request(`/databases/${uuid}/envs/bulk`, {
1001
+ method: 'PATCH',
1002
+ body: JSON.stringify(data),
1003
+ });
1004
+ }
1005
+ async deleteDatabaseEnvVar(uuid, envUuid) {
1006
+ return this.request(`/databases/${uuid}/envs/${envUuid}`, {
1007
+ method: 'DELETE',
1008
+ });
1009
+ }
1010
+ // ===========================================================================
1011
+ // Database Storage endpoints
1012
+ // ===========================================================================
1013
+ async listDatabaseStorages(uuid) {
1014
+ return this.request(`/databases/${uuid}/storages`);
1015
+ }
1016
+ async createDatabaseStorage(uuid, data) {
1017
+ return this.request(`/databases/${uuid}/storages`, {
1018
+ method: 'POST',
1019
+ body: JSON.stringify(data),
1020
+ });
1021
+ }
1022
+ async updateDatabaseStorage(uuid, data) {
1023
+ return this.request(`/databases/${uuid}/storages`, {
1024
+ method: 'PATCH',
1025
+ body: JSON.stringify(data),
1026
+ });
1027
+ }
1028
+ async deleteDatabaseStorage(uuid, storageUuid) {
1029
+ return this.request(`/databases/${uuid}/storages/${storageUuid}`, {
1030
+ method: 'DELETE',
1031
+ });
1032
+ }
1033
+ // ===========================================================================
1034
+ // Delete Backup Execution endpoint
1035
+ // ===========================================================================
1036
+ async deleteBackupExecution(databaseUuid, backupUuid, executionUuid) {
1037
+ return this.request(`/databases/${databaseUuid}/backups/${backupUuid}/executions/${executionUuid}`, { method: 'DELETE' });
1038
+ }
1039
+ // ===========================================================================
1040
+ // Service Environment Variable (bulk) endpoint
1041
+ // ===========================================================================
1042
+ async bulkUpdateServiceEnvVars(uuid, data) {
1043
+ return this.request(`/services/${uuid}/envs/bulk`, {
1044
+ method: 'PATCH',
1045
+ body: JSON.stringify(data),
1046
+ });
1047
+ }
1048
+ // ===========================================================================
1049
+ // Service Storage endpoints
1050
+ // ===========================================================================
1051
+ async listServiceStorages(uuid) {
1052
+ return this.request(`/services/${uuid}/storages`);
1053
+ }
1054
+ async createServiceStorage(uuid, data) {
1055
+ return this.request(`/services/${uuid}/storages`, {
1056
+ method: 'POST',
1057
+ body: JSON.stringify(data),
1058
+ });
1059
+ }
1060
+ async updateServiceStorage(uuid, data) {
1061
+ return this.request(`/services/${uuid}/storages`, {
1062
+ method: 'PATCH',
1063
+ body: JSON.stringify(data),
1064
+ });
1065
+ }
1066
+ async deleteServiceStorage(uuid, storageUuid) {
1067
+ return this.request(`/services/${uuid}/storages/${storageUuid}`, {
1068
+ method: 'DELETE',
1069
+ });
1070
+ }
1071
+ // ===========================================================================
1072
+ // Service Scheduled Task endpoints
1073
+ // ===========================================================================
1074
+ async listServiceScheduledTasks(uuid) {
1075
+ return this.request(`/services/${uuid}/scheduled-tasks`);
1076
+ }
1077
+ async createServiceScheduledTask(uuid, data) {
1078
+ return this.request(`/services/${uuid}/scheduled-tasks`, {
1079
+ method: 'POST',
1080
+ body: JSON.stringify(data),
1081
+ });
1082
+ }
1083
+ async updateServiceScheduledTask(uuid, taskUuid, data) {
1084
+ return this.request(`/services/${uuid}/scheduled-tasks/${taskUuid}`, {
1085
+ method: 'PATCH',
1086
+ body: JSON.stringify(data),
1087
+ });
1088
+ }
1089
+ async deleteServiceScheduledTask(uuid, taskUuid) {
1090
+ return this.request(`/services/${uuid}/scheduled-tasks/${taskUuid}`, {
1091
+ method: 'DELETE',
1092
+ });
1093
+ }
1094
+ async listServiceScheduledTaskExecutions(uuid, taskUuid) {
1095
+ return this.request(`/services/${uuid}/scheduled-tasks/${taskUuid}/executions`);
1096
+ }
1097
+ // ===========================================================================
1098
+ // Hetzner Cloud endpoints
1099
+ // ===========================================================================
1100
+ async listHetznerLocations(tokenUuid) {
1101
+ return this.request(`/hetzner/locations?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1102
+ }
1103
+ async listHetznerServerTypes(tokenUuid) {
1104
+ return this.request(`/hetzner/server-types?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1105
+ }
1106
+ async listHetznerImages(tokenUuid) {
1107
+ return this.request(`/hetzner/images?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1108
+ }
1109
+ async listHetznerSSHKeys(tokenUuid) {
1110
+ return this.request(`/hetzner/ssh-keys?cloud_provider_token_uuid=${encodeURIComponent(tokenUuid)}`);
1111
+ }
1112
+ async createHetznerServer(data) {
1113
+ return this.request('/servers/hetzner', {
1114
+ method: 'POST',
1115
+ body: JSON.stringify(data),
1116
+ });
1117
+ }
1118
+ // ===========================================================================
1119
+ // GitHub App Repository endpoints
1120
+ // ===========================================================================
1121
+ async listGitHubAppRepositories(githubAppId) {
1122
+ const response = await this.request(`/github-apps/${githubAppId}/repositories`);
1123
+ return response.repositories ?? [];
1124
+ }
1125
+ async listGitHubAppBranches(githubAppId, owner, repo) {
1126
+ return this.request(`/github-apps/${githubAppId}/repositories/${encodeURIComponent(owner)}/${encodeURIComponent(repo)}/branches`);
1127
+ }
1128
+ // ===========================================================================
1129
+ // Resources endpoint
1130
+ // ===========================================================================
1131
+ async listResources() {
1132
+ return this.request('/resources');
1133
+ }
1134
+ // ===========================================================================
1135
+ // Health endpoint
1136
+ // ===========================================================================
1137
+ async getHealth() {
1138
+ return this.request('/health');
1139
+ }
1140
+ // ===========================================================================
1141
+ // API Enable/Disable endpoints
1142
+ // ===========================================================================
1143
+ async enableApi() {
1144
+ return this.request('/enable', { method: 'GET' });
1145
+ }
1146
+ async disableApi() {
1147
+ return this.request('/disable', { method: 'GET' });
1148
+ }
1149
+ // ===========================================================================
925
1150
  // Deployment Control endpoints
926
1151
  // ===========================================================================
927
1152
  async cancelDeployment(uuid) {