@mks2508/coolify-mks-cli-mcp 0.6.3 → 0.9.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 (75) hide show
  1. package/dist/cli/coolify-state.d.ts +101 -5
  2. package/dist/cli/coolify-state.d.ts.map +1 -1
  3. package/dist/cli/index.js +23165 -11543
  4. package/dist/cli/ui/highlighter.d.ts +28 -0
  5. package/dist/cli/ui/highlighter.d.ts.map +1 -0
  6. package/dist/cli/ui/index.d.ts +9 -0
  7. package/dist/cli/ui/index.d.ts.map +1 -0
  8. package/dist/cli/ui/spinners.d.ts +100 -0
  9. package/dist/cli/ui/spinners.d.ts.map +1 -0
  10. package/dist/cli/ui/tables.d.ts +103 -0
  11. package/dist/cli/ui/tables.d.ts.map +1 -0
  12. package/dist/coolify/config.d.ts +25 -0
  13. package/dist/coolify/config.d.ts.map +1 -1
  14. package/dist/coolify/index.d.ts +139 -12
  15. package/dist/coolify/index.d.ts.map +1 -1
  16. package/dist/coolify/types.d.ts +160 -2
  17. package/dist/coolify/types.d.ts.map +1 -1
  18. package/dist/examples/demo-ui.d.ts +8 -0
  19. package/dist/examples/demo-ui.d.ts.map +1 -0
  20. package/dist/index.cjs +2580 -230
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.js +2598 -226
  23. package/dist/index.js.map +1 -1
  24. package/dist/sdk.d.ts +96 -7
  25. package/dist/sdk.d.ts.map +1 -1
  26. package/dist/server/stdio.js +475 -73
  27. package/dist/tools/definitions.d.ts.map +1 -1
  28. package/dist/tools/handlers.d.ts.map +1 -1
  29. package/dist/utils/env-parser.d.ts +24 -0
  30. package/dist/utils/env-parser.d.ts.map +1 -0
  31. package/dist/utils/format.d.ts +32 -0
  32. package/dist/utils/format.d.ts.map +1 -1
  33. package/package.json +17 -4
  34. package/src/cli/actions.ts +9 -2
  35. package/src/cli/commands/create.ts +332 -24
  36. package/src/cli/commands/db.ts +37 -0
  37. package/src/cli/commands/delete.ts +6 -2
  38. package/src/cli/commands/deploy.ts +347 -49
  39. package/src/cli/commands/deployments.ts +6 -2
  40. package/src/cli/commands/diagnose.ts +3 -3
  41. package/src/cli/commands/env.ts +424 -31
  42. package/src/cli/commands/exec.ts +6 -2
  43. package/src/cli/commands/init.ts +991 -0
  44. package/src/cli/commands/logs.ts +224 -24
  45. package/src/cli/commands/main-menu.ts +21 -0
  46. package/src/cli/commands/projects.ts +312 -29
  47. package/src/cli/commands/restart.ts +6 -2
  48. package/src/cli/commands/service-logs.ts +14 -0
  49. package/src/cli/commands/show.ts +45 -12
  50. package/src/cli/commands/start.ts +6 -2
  51. package/src/cli/commands/status.ts +554 -0
  52. package/src/cli/commands/stop.ts +6 -2
  53. package/src/cli/commands/svc.ts +7 -1
  54. package/src/cli/commands/update.ts +79 -2
  55. package/src/cli/commands/volumes.ts +293 -0
  56. package/src/cli/coolify-state.ts +203 -12
  57. package/src/cli/index.ts +138 -11
  58. package/src/cli/name-resolver.ts +228 -0
  59. package/src/cli/ui/banner.ts +276 -0
  60. package/src/cli/ui/highlighter.ts +176 -0
  61. package/src/cli/ui/index.ts +9 -0
  62. package/src/cli/ui/prompts.ts +155 -0
  63. package/src/cli/ui/screen.ts +630 -0
  64. package/src/cli/ui/select.ts +280 -0
  65. package/src/cli/ui/spinners.ts +256 -0
  66. package/src/cli/ui/tables.ts +407 -0
  67. package/src/coolify/config.ts +75 -0
  68. package/src/coolify/index.ts +565 -101
  69. package/src/coolify/types.ts +165 -2
  70. package/src/examples/demo-ui.ts +78 -0
  71. package/src/sdk.ts +211 -1
  72. package/src/tools/definitions.ts +22 -0
  73. package/src/tools/handlers.ts +19 -0
  74. package/src/utils/env-parser.ts +45 -0
  75. package/src/utils/format.ts +178 -0
@@ -1,31 +1,51 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
2
3
  var __create = Object.create;
3
4
  var __getProtoOf = Object.getPrototypeOf;
4
5
  var __defProp = Object.defineProperty;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
7
13
  var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
8
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
23
  for (let key of __getOwnPropNames(mod))
11
24
  if (!__hasOwnProp.call(to, key))
12
25
  __defProp(to, key, {
13
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
14
27
  enumerable: true
15
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
16
31
  return to;
17
32
  };
18
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
+ var __returnValue = (v) => v;
35
+ function __exportSetter(name, newValue) {
36
+ this[name] = __returnValue.bind(null, newValue);
37
+ }
19
38
  var __export = (target, all) => {
20
39
  for (var name in all)
21
40
  __defProp(target, name, {
22
41
  get: all[name],
23
42
  enumerable: true,
24
43
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
44
+ set: __exportSetter.bind(all, name)
26
45
  });
27
46
  };
28
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
48
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
29
49
 
30
50
  // ../../../node_modules/.bun/ajv@8.17.1/node_modules/ajv/dist/compile/codegen/code.js
31
51
  var require_code = __commonJS((exports) => {
@@ -6277,7 +6297,7 @@ var require_formats = __commonJS((exports) => {
6277
6297
  }
6278
6298
  var TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i;
6279
6299
  function getTime(strictTimeZone) {
6280
- return function time(str) {
6300
+ return function time3(str) {
6281
6301
  const matches = TIME.exec(str);
6282
6302
  if (!matches)
6283
6303
  return false;
@@ -12971,6 +12991,11 @@ Redeploy after updating to apply changes.`,
12971
12991
  type: "string",
12972
12992
  description: 'Base directory for build context (default: "/")'
12973
12993
  },
12994
+ watchPaths: {
12995
+ type: "string",
12996
+ description: 'Watch paths for selective auto-deploy. Newline-separated globs (e.g. "src/**\\npackages/**"). Set to empty string or null to clear.',
12997
+ nullable: true
12998
+ },
12974
12999
  dockerComposeDomains: {
12975
13000
  type: "string",
12976
13001
  description: 'Docker Compose domains JSON: { "service-name": { "domain": "https://..." } }'
@@ -12979,6 +13004,22 @@ Redeploy after updating to apply changes.`,
12979
13004
  required: ["uuid"]
12980
13005
  }
12981
13006
  },
13007
+ {
13008
+ name: "get_application",
13009
+ description: `Get detailed information about a Coolify application including settings (auto-deploy, force HTTPS) and watch paths.
13010
+
13011
+ Returns full application details that list_applications doesn't include.`,
13012
+ inputSchema: {
13013
+ type: "object",
13014
+ properties: {
13015
+ uuid: {
13016
+ type: "string",
13017
+ description: "Application UUID"
13018
+ }
13019
+ },
13020
+ required: ["uuid"]
13021
+ }
13022
+ },
12982
13023
  {
12983
13024
  name: "set_domains",
12984
13025
  description: `Set domains/FQDN for a Coolify application.
@@ -18445,6 +18486,7 @@ async function loadConfig() {
18445
18486
  return err(error2 instanceof Error ? error2 : new Error(String(error2)));
18446
18487
  }
18447
18488
  }
18489
+ var SETTINGS_CACHE_FILE = join(CONFIG_DIR, "app-settings-cache.json");
18448
18490
 
18449
18491
  // src/coolify/index.ts
18450
18492
  var log = component("CoolifyService");
@@ -18506,7 +18548,10 @@ class CoolifyService {
18506
18548
  let data;
18507
18549
  try {
18508
18550
  data = text ? JSON.parse(text) : undefined;
18509
- } catch {
18551
+ } catch (parseErr) {
18552
+ const preview = text ? text.slice(0, 200) : "(empty body)";
18553
+ const method = options.method ?? "GET";
18554
+ log.warn(`Failed to parse JSON response from ${method} ${endpoint} (status ${response.status}): ${parseErr instanceof Error ? parseErr.message : String(parseErr)}. Body preview: ${preview}`);
18510
18555
  if (!response.ok) {
18511
18556
  return {
18512
18557
  error: text || `HTTP ${response.status}`,
@@ -18514,6 +18559,11 @@ class CoolifyService {
18514
18559
  durationMs
18515
18560
  };
18516
18561
  }
18562
+ return {
18563
+ error: `Response was not valid JSON (status ${response.status}): ${preview}`,
18564
+ status: response.status,
18565
+ durationMs
18566
+ };
18517
18567
  }
18518
18568
  if (!response.ok) {
18519
18569
  const parsed = data;
@@ -18580,8 +18630,8 @@ class CoolifyService {
18580
18630
  "private-github-app": "/applications/private-github-app",
18581
18631
  "private-deploy-key": "/applications/private-deploy-key",
18582
18632
  dockerfile: "/applications/dockerfile",
18583
- "docker-image": "/applications/docker-image",
18584
- "docker-compose": "/applications/docker-compose",
18633
+ "docker-image": "/applications/dockerimage",
18634
+ "docker-compose": "/applications/dockercompose",
18585
18635
  dockerimage: "/applications/dockerimage",
18586
18636
  dockercompose: "/applications/dockercompose"
18587
18637
  };
@@ -18591,33 +18641,40 @@ class CoolifyService {
18591
18641
  description: options.description,
18592
18642
  project_uuid: options.projectUuid,
18593
18643
  environment_uuid: options.environmentUuid,
18644
+ environment_name: options.environmentName,
18594
18645
  server_uuid: options.serverUuid
18595
18646
  };
18647
+ if (options.githubRepoUrl) {
18648
+ body.git_repository = options.githubRepoUrl;
18649
+ }
18596
18650
  if (appType === "public" || appType === "private-github-app" || appType === "private-deploy-key") {
18597
- if (options.githubRepoUrl) {
18598
- body.git_repository = options.githubRepoUrl.replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
18599
- }
18600
18651
  if (options.githubAppUuid) {
18601
18652
  body.github_app_uuid = options.githubAppUuid;
18602
18653
  }
18654
+ if (appType === "private-deploy-key" && options.privateKeyUuid) {
18655
+ body.private_key_uuid = options.privateKeyUuid;
18656
+ }
18603
18657
  body.git_branch = options.branch || "main";
18604
18658
  body.build_pack = options.buildPack || "dockerfile";
18605
18659
  if (options.portsExposes) {
18606
18660
  body.ports_exposes = options.portsExposes;
18607
18661
  }
18608
18662
  if (options.dockerfileLocation) {
18609
- body.dockerfile_location = options.dockerfileLocation;
18663
+ const p = options.dockerfileLocation;
18664
+ body.dockerfile_location = p.startsWith("/") ? p : `/${p}`;
18610
18665
  }
18611
18666
  if (options.dockerComposeLocation) {
18612
- body.docker_compose_location = options.dockerComposeLocation;
18667
+ const p = options.dockerComposeLocation;
18668
+ body.docker_compose_location = p.startsWith("/") ? p : `/${p}`;
18613
18669
  }
18614
18670
  if (options.baseDirectory) {
18615
- body.base_directory = options.baseDirectory;
18671
+ const p = options.baseDirectory;
18672
+ body.base_directory = p.startsWith("/") ? p : `/${p}`;
18616
18673
  }
18617
18674
  } else if (appType === "docker-image" && options.dockerImage) {
18618
- body.docker_image = options.dockerImage;
18675
+ body.docker_registry_image_name = options.dockerImage;
18619
18676
  } else if (appType === "docker-compose" && options.dockerCompose) {
18620
- body.docker_compose = options.dockerCompose;
18677
+ body.docker_compose_raw = options.dockerCompose;
18621
18678
  }
18622
18679
  log.debug(`Create application body: ${JSON.stringify(body, null, 2)}`);
18623
18680
  log.debug(`Endpoint: POST ${endpoint}`);
@@ -18638,37 +18695,17 @@ class CoolifyService {
18638
18695
  }
18639
18696
  async setEnvironmentVariables(appUuid, envVars) {
18640
18697
  log.info(`Setting ${Object.keys(envVars).length} environment variables for ${appUuid}`);
18641
- for (const [key, value] of Object.entries(envVars)) {
18642
- const result = await this.request(`/applications/${appUuid}/envs`, {
18643
- method: "POST",
18644
- body: JSON.stringify({ key, value, is_preview: false })
18645
- });
18646
- if (result.error && result.error.includes("already exists")) {
18647
- const listResult = await this.request(`/applications/${appUuid}/envs`);
18648
- const existing = listResult.data?.find((v) => v.key === key);
18649
- if (existing) {
18650
- await this.request(`/applications/${appUuid}/envs/${existing.uuid}`, {
18651
- method: "DELETE"
18652
- });
18653
- const repost = await this.request(`/applications/${appUuid}/envs`, {
18654
- method: "POST",
18655
- body: JSON.stringify({ key, value, is_preview: false })
18656
- });
18657
- if (repost.error) {
18658
- log.error(`Failed to update env var ${key}: ${repost.error}`);
18659
- return err(new Error(`Failed to update ${key}: ${repost.error}`));
18660
- }
18661
- log.debug(`Updated existing env var: ${key}`);
18662
- } else {
18663
- log.error(`Failed to set env var ${key}: ${result.error}`);
18664
- return err(new Error(`Failed to set ${key}: ${result.error}`));
18665
- }
18666
- } else if (result.error) {
18667
- log.error(`Failed to set env var ${key}: ${result.error}`);
18668
- return err(new Error(`Failed to set ${key}: ${result.error}`));
18669
- }
18698
+ const vars = Object.entries(envVars).map(([key, value]) => ({
18699
+ key,
18700
+ value,
18701
+ is_buildtime: false,
18702
+ is_runtime: true
18703
+ }));
18704
+ const result = await this.bulkUpdateEnvironmentVariables(appUuid, vars);
18705
+ if (isErr(result)) {
18706
+ return err(result.error);
18670
18707
  }
18671
- log.success(`${Object.keys(envVars).length} environment variables set`);
18708
+ log.success(`${vars.length} environment variables set`);
18672
18709
  return ok(undefined);
18673
18710
  }
18674
18711
  async getEnvironmentVariables(appUuid) {
@@ -18681,33 +18718,18 @@ class CoolifyService {
18681
18718
  log.success(`Environment variables retrieved for ${appUuid}`);
18682
18719
  return ok(result.data || []);
18683
18720
  }
18684
- async setEnvironmentVariable(appUuid, key, value, _isBuildTime = false) {
18685
- log.info(`Setting environment variable ${key} for ${appUuid}`);
18686
- const existingVars = await this.getEnvironmentVariables(appUuid);
18687
- if (isErr(existingVars)) {
18688
- return err(existingVars.error);
18689
- }
18690
- const exists = existingVars.value.some((ev) => ev.key === key);
18691
- if (exists) {
18692
- log.debug(`Variable ${key} exists, using PATCH to update`);
18693
- const result = await this.request(`/applications/${appUuid}/envs`, {
18694
- method: "PATCH",
18695
- body: JSON.stringify({ key, value })
18696
- });
18697
- if (result.error) {
18698
- log.error(`Failed to update env var: ${result.error}`);
18699
- return err(new Error(result.error));
18700
- }
18701
- } else {
18702
- log.debug(`Variable ${key} does not exist, using POST to create`);
18703
- const result = await this.request(`/applications/${appUuid}/envs`, {
18704
- method: "POST",
18705
- body: JSON.stringify({ key, value })
18706
- });
18707
- if (result.error) {
18708
- log.error(`Failed to create env var: ${result.error}`);
18709
- return err(new Error(result.error));
18721
+ async setEnvironmentVariable(appUuid, key, value, isBuildTime = false) {
18722
+ log.info(`Setting environment variable ${key} for ${appUuid} (buildtime: ${isBuildTime})`);
18723
+ const result = await this.bulkUpdateEnvironmentVariables(appUuid, [
18724
+ {
18725
+ key,
18726
+ value,
18727
+ is_buildtime: isBuildTime,
18728
+ is_runtime: !isBuildTime
18710
18729
  }
18730
+ ]);
18731
+ if (isErr(result)) {
18732
+ return err(result.error);
18711
18733
  }
18712
18734
  log.success(`Environment variable ${key} set for ${appUuid}`);
18713
18735
  return ok(undefined);
@@ -18781,6 +18803,23 @@ class CoolifyService {
18781
18803
  return err(new Error(result.error));
18782
18804
  return ok(result.data || []);
18783
18805
  }
18806
+ async listGithubAppsAll(perPage = 50) {
18807
+ const allApps = [];
18808
+ let page = 1;
18809
+ while (true) {
18810
+ const result = await this.listGithubApps(page, perPage);
18811
+ if (isErr(result))
18812
+ return err(result.error);
18813
+ const apps = result.value;
18814
+ if (apps.length === 0)
18815
+ break;
18816
+ allApps.push(...apps);
18817
+ if (apps.length < perPage)
18818
+ break;
18819
+ page++;
18820
+ }
18821
+ return ok(allApps);
18822
+ }
18784
18823
  async listProjects(page, perPage) {
18785
18824
  let endpoint = "/projects";
18786
18825
  const params = new URLSearchParams;
@@ -18916,10 +18955,32 @@ class CoolifyService {
18916
18955
  body.domains = options.domains;
18917
18956
  if (options.dockerComposeDomains)
18918
18957
  body.docker_compose_domains = options.dockerComposeDomains;
18958
+ if (options.dockerComposeRaw !== undefined)
18959
+ body.docker_compose_raw = options.dockerComposeRaw;
18919
18960
  if (options.isForceHttpsEnabled !== undefined)
18920
18961
  body.is_force_https_enabled = options.isForceHttpsEnabled;
18921
18962
  if (options.isAutoDeployEnabled !== undefined)
18922
18963
  body.is_auto_deploy_enabled = options.isAutoDeployEnabled;
18964
+ if (options.watchPaths !== undefined)
18965
+ body.watch_paths = options.watchPaths;
18966
+ if (options.healthCheckEnabled !== undefined)
18967
+ body.health_check_enabled = options.healthCheckEnabled;
18968
+ if (options.healthCheckPath)
18969
+ body.health_check_path = options.healthCheckPath;
18970
+ if (options.healthCheckPort)
18971
+ body.health_check_port = String(options.healthCheckPort);
18972
+ if (options.healthCheckMethod)
18973
+ body.health_check_method = options.healthCheckMethod;
18974
+ if (options.healthCheckInterval)
18975
+ body.health_check_interval = options.healthCheckInterval;
18976
+ if (options.healthCheckTimeout)
18977
+ body.health_check_timeout = options.healthCheckTimeout;
18978
+ if (options.healthCheckRetries)
18979
+ body.health_check_retries = options.healthCheckRetries;
18980
+ if (options.healthCheckStartPeriod)
18981
+ body.health_check_start_period = options.healthCheckStartPeriod;
18982
+ if (options.healthCheckReturnCode)
18983
+ body.health_check_return_code = options.healthCheckReturnCode;
18923
18984
  const result = await this.request(`/applications/${appUuid}`, {
18924
18985
  method: "PATCH",
18925
18986
  body: JSON.stringify(body)
@@ -18972,7 +19033,7 @@ class CoolifyService {
18972
19033
  log.info(`Bulk updating ${envVars.length} env vars for ${appUuid}`);
18973
19034
  const result = await this.request(`/applications/${appUuid}/envs/bulk`, {
18974
19035
  method: "PATCH",
18975
- body: JSON.stringify(envVars)
19036
+ body: JSON.stringify({ data: envVars })
18976
19037
  });
18977
19038
  if (result.error) {
18978
19039
  log.error(`Failed to bulk update env vars: ${result.error}`);
@@ -18981,6 +19042,19 @@ class CoolifyService {
18981
19042
  log.success(`Bulk updated ${envVars.length} env vars for ${appUuid}`);
18982
19043
  return ok(result.data || { message: "Environment variables updated" });
18983
19044
  }
19045
+ async bulkUpdateDatabaseEnvVars(databaseUuid, envVars) {
19046
+ log.info(`Bulk updating ${envVars.length} env vars for database ${databaseUuid}`);
19047
+ const result = await this.request(`/databases/${databaseUuid}/envs/bulk`, {
19048
+ method: "PATCH",
19049
+ body: JSON.stringify({ data: envVars })
19050
+ });
19051
+ if (result.error) {
19052
+ log.error(`Failed to bulk update database env vars: ${result.error}`);
19053
+ return err(new Error(result.error));
19054
+ }
19055
+ log.success(`Bulk updated ${envVars.length} env vars for database ${databaseUuid}`);
19056
+ return ok(result.data || { message: "Environment variables updated" });
19057
+ }
18984
19058
  async getApplicationDeploymentHistory(appUuid) {
18985
19059
  log.info(`Getting deployment history for ${appUuid}`);
18986
19060
  const result = await this.request(`/deployments/applications/${appUuid}`);
@@ -19243,6 +19317,133 @@ class CoolifyService {
19243
19317
  log.success(`Service deleted: ${uuid2}`);
19244
19318
  return ok({ success: true, message: "Service deleted" });
19245
19319
  }
19320
+ async getInfrastructureTree() {
19321
+ log.info("Building infrastructure tree");
19322
+ const [projectsResult, appsResult, dbsResult, svcsResult, serversResult] = await Promise.all([
19323
+ this.listProjects(),
19324
+ this.listApplications(),
19325
+ this.listDatabases(),
19326
+ this.listServices(),
19327
+ this.listServers()
19328
+ ]);
19329
+ if (isErr(projectsResult))
19330
+ return err(projectsResult.error);
19331
+ if (isErr(appsResult))
19332
+ return err(appsResult.error);
19333
+ if (isErr(serversResult))
19334
+ return err(serversResult.error);
19335
+ const projects = projectsResult.value;
19336
+ const apps = appsResult.value;
19337
+ const dbs = isErr(dbsResult) ? [] : dbsResult.value;
19338
+ const svcs = isErr(svcsResult) ? [] : svcsResult.value;
19339
+ const servers = serversResult.value;
19340
+ const envResults = await Promise.allSettled(projects.map((p) => this.getProjectEnvironments(p.uuid)));
19341
+ const envIdMap = new Map;
19342
+ for (let i = 0;i < projects.length; i++) {
19343
+ const envResult = envResults[i];
19344
+ if (envResult.status === "fulfilled" && !isErr(envResult.value)) {
19345
+ for (const env of envResult.value.value) {
19346
+ envIdMap.set(env.id, {
19347
+ projectUuid: projects[i].uuid,
19348
+ envName: env.name,
19349
+ envUuid: env.uuid
19350
+ });
19351
+ }
19352
+ }
19353
+ }
19354
+ const projectNodes = projects.map((p) => ({
19355
+ uuid: p.uuid,
19356
+ name: p.name,
19357
+ description: p.description,
19358
+ environments: []
19359
+ }));
19360
+ const projectMap = new Map;
19361
+ for (const node of projectNodes) {
19362
+ projectMap.set(node.uuid, node);
19363
+ }
19364
+ const envNodeMap = new Map;
19365
+ for (const [envId, info] of envIdMap) {
19366
+ const project = projectMap.get(info.projectUuid);
19367
+ if (!project)
19368
+ continue;
19369
+ let envNode = project.environments.find((e) => e.id === envId);
19370
+ if (!envNode) {
19371
+ envNode = {
19372
+ id: envId,
19373
+ uuid: info.envUuid,
19374
+ name: info.envName,
19375
+ resources: []
19376
+ };
19377
+ project.environments.push(envNode);
19378
+ }
19379
+ envNodeMap.set(envId, envNode);
19380
+ }
19381
+ for (const app of apps) {
19382
+ const envNode = app.environment_id ? envNodeMap.get(app.environment_id) : undefined;
19383
+ const resource = {
19384
+ uuid: app.uuid,
19385
+ name: app.name,
19386
+ kind: "app",
19387
+ status: app.status,
19388
+ fqdn: app.fqdn
19389
+ };
19390
+ if (envNode) {
19391
+ envNode.resources.push(resource);
19392
+ }
19393
+ }
19394
+ for (const db of dbs) {
19395
+ const envNode = db.environment_id ? envNodeMap.get(db.environment_id) : undefined;
19396
+ const resource = {
19397
+ uuid: db.uuid,
19398
+ name: db.name,
19399
+ kind: "database",
19400
+ status: db.status,
19401
+ dbType: db.type
19402
+ };
19403
+ if (envNode) {
19404
+ envNode.resources.push(resource);
19405
+ }
19406
+ }
19407
+ for (const svc of svcs) {
19408
+ const envNode = svc.environment_id ? envNodeMap.get(svc.environment_id) : undefined;
19409
+ const resource = {
19410
+ uuid: svc.uuid,
19411
+ name: svc.name,
19412
+ kind: "service",
19413
+ status: svc.status
19414
+ };
19415
+ if (envNode) {
19416
+ envNode.resources.push(resource);
19417
+ }
19418
+ }
19419
+ const populatedProjects = projectNodes.filter((p) => p.environments.some((e) => e.resources.length > 0));
19420
+ const allStatuses = [
19421
+ ...apps.map((a) => a.status),
19422
+ ...dbs.map((d) => d.status),
19423
+ ...svcs.map((s) => s.status)
19424
+ ];
19425
+ const counts = {
19426
+ projects: populatedProjects.length,
19427
+ apps: apps.length,
19428
+ databases: dbs.length,
19429
+ services: svcs.length,
19430
+ healthy: allStatuses.filter((s) => s.includes("healthy")).length,
19431
+ running: allStatuses.filter((s) => s.startsWith("running") && !s.includes("healthy")).length,
19432
+ stopped: allStatuses.filter((s) => s.includes("exited")).length,
19433
+ unhealthy: allStatuses.filter((s) => s.includes("unhealthy")).length
19434
+ };
19435
+ const server = servers[0] || { name: "Unknown" };
19436
+ log.success(`Infrastructure tree built: ${counts.projects} projects, ${counts.apps} apps, ${counts.databases} dbs, ${counts.services} svcs`);
19437
+ return ok({
19438
+ server: {
19439
+ name: server.name,
19440
+ ip: server.ip,
19441
+ uuid: server.uuid
19442
+ },
19443
+ projects: populatedProjects,
19444
+ counts
19445
+ });
19446
+ }
19246
19447
  async startService(uuid2) {
19247
19448
  log.info(`Starting service ${uuid2}`);
19248
19449
  const result = await this.request(`/services/${uuid2}/start`, { method: "GET" });
@@ -19273,12 +19474,76 @@ class CoolifyService {
19273
19474
  return err(new Error(result.error));
19274
19475
  return ok(result.data || []);
19275
19476
  }
19477
+ async bulkUpdateServiceEnvVars(serviceUuid, envVars) {
19478
+ log.info(`Bulk updating ${envVars.length} env vars for service ${serviceUuid}`);
19479
+ const result = await this.request(`/services/${serviceUuid}/envs/bulk`, {
19480
+ method: "PATCH",
19481
+ body: JSON.stringify({ data: envVars })
19482
+ });
19483
+ if (result.error) {
19484
+ log.error(`Failed to bulk update service env vars: ${result.error}`);
19485
+ return err(new Error(result.error));
19486
+ }
19487
+ log.success(`Bulk updated ${envVars.length} env vars for service ${serviceUuid}`);
19488
+ return ok(result.data || { message: "Environment variables updated" });
19489
+ }
19276
19490
  async createServiceEnvVar(uuid2, data) {
19277
19491
  const result = await this.request(`/services/${uuid2}/envs`, { method: "POST", body: JSON.stringify(data) });
19278
19492
  if (result.error)
19279
19493
  return err(new Error(result.error));
19280
19494
  return ok(result.data);
19281
19495
  }
19496
+ async deleteServiceEnvVar(serviceUuid, key) {
19497
+ log.info(`Deleting environment variable ${key} from service ${serviceUuid}`);
19498
+ const envVarsResult = await this.listServiceEnvVars(serviceUuid);
19499
+ if (isErr(envVarsResult)) {
19500
+ return err(envVarsResult.error);
19501
+ }
19502
+ const envVar = envVarsResult.value.find((ev) => ev.key === key);
19503
+ if (!envVar) {
19504
+ log.error(`Environment variable ${key} not found on service ${serviceUuid}`);
19505
+ return err(new Error(`Environment variable ${key} not found`));
19506
+ }
19507
+ const result = await this.request(`/services/${serviceUuid}/envs/${envVar.uuid}`, {
19508
+ method: "DELETE"
19509
+ });
19510
+ if (result.error) {
19511
+ log.error(`Failed to delete service env var: ${result.error}`);
19512
+ return err(new Error(result.error));
19513
+ }
19514
+ log.success(`Environment variable ${key} deleted from service ${serviceUuid}`);
19515
+ return ok(undefined);
19516
+ }
19517
+ async listDatabaseEnvVars(databaseUuid) {
19518
+ log.info(`Listing env vars for database ${databaseUuid}`);
19519
+ const result = await this.request(`/databases/${databaseUuid}/envs`);
19520
+ if (result.error) {
19521
+ log.error(`Failed to list database env vars: ${result.error}`);
19522
+ return err(new Error(result.error));
19523
+ }
19524
+ return ok(result.data || []);
19525
+ }
19526
+ async deleteDatabaseEnvVar(databaseUuid, key) {
19527
+ log.info(`Deleting environment variable ${key} from database ${databaseUuid}`);
19528
+ const envVarsResult = await this.listDatabaseEnvVars(databaseUuid);
19529
+ if (isErr(envVarsResult)) {
19530
+ return err(envVarsResult.error);
19531
+ }
19532
+ const envVar = envVarsResult.value.find((ev) => ev.key === key);
19533
+ if (!envVar) {
19534
+ log.error(`Environment variable ${key} not found on database ${databaseUuid}`);
19535
+ return err(new Error(`Environment variable ${key} not found`));
19536
+ }
19537
+ const result = await this.request(`/databases/${databaseUuid}/envs/${envVar.uuid}`, {
19538
+ method: "DELETE"
19539
+ });
19540
+ if (result.error) {
19541
+ log.error(`Failed to delete database env var: ${result.error}`);
19542
+ return err(new Error(result.error));
19543
+ }
19544
+ log.success(`Environment variable ${key} deleted from database ${databaseUuid}`);
19545
+ return ok(undefined);
19546
+ }
19282
19547
  async getServerResources(serverUuid) {
19283
19548
  log.info(`Getting resources for server ${serverUuid}`);
19284
19549
  const result = await this.request(`/servers/${serverUuid}/resources`);
@@ -19504,6 +19769,17 @@ class CoolifyService {
19504
19769
  log.success(`Listed ${deployments.length} deployments for ${appUuid}`);
19505
19770
  return ok(deployments);
19506
19771
  }
19772
+ async getApplication(appUuid) {
19773
+ log.info(`Getting application details: ${appUuid}`);
19774
+ const result = await this.request(`/applications/${appUuid}`);
19775
+ if (result.error) {
19776
+ return err(new Error(result.error));
19777
+ }
19778
+ if (!result.data) {
19779
+ return err(new Error("Application not found"));
19780
+ }
19781
+ return ok(result.data);
19782
+ }
19507
19783
  async resolveApplication(query) {
19508
19784
  log.info(`Resolving application: ${query}`);
19509
19785
  if (this.isLikelyUuid(query)) {
@@ -19784,6 +20060,28 @@ Please use the full UUID.`));
19784
20060
  }
19785
20061
  }
19786
20062
 
20063
+ // src/utils/env-parser.ts
20064
+ function parseEnvContent(content) {
20065
+ const envVars = new Map;
20066
+ for (const line of content.split(`
20067
+ `)) {
20068
+ const trimmed = line.trim();
20069
+ if (!trimmed || trimmed.startsWith("#"))
20070
+ continue;
20071
+ const eq = trimmed.indexOf("=");
20072
+ if (eq === -1)
20073
+ continue;
20074
+ const key = trimmed.slice(0, eq).trim();
20075
+ let value = trimmed.slice(eq + 1).trim();
20076
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
20077
+ value = value.slice(1, -1);
20078
+ }
20079
+ if (key)
20080
+ envVars.set(key, value);
20081
+ }
20082
+ return envVars;
20083
+ }
20084
+
19787
20085
  // src/sdk.ts
19788
20086
  function unwrap(result) {
19789
20087
  if (isErr(result))
@@ -19802,6 +20100,9 @@ class ApplicationsResource {
19802
20100
  async listSummaries() {
19803
20101
  return unwrap(await this.svc.listApplicationSummaries());
19804
20102
  }
20103
+ async get(uuid2) {
20104
+ return unwrap(await this.svc.getApplication(uuid2));
20105
+ }
19805
20106
  async resolve(query) {
19806
20107
  return unwrap(await this.svc.resolveApplication(query));
19807
20108
  }
@@ -19847,6 +20148,77 @@ class ApplicationsResource {
19847
20148
  async deleteEnv(uuid2, key) {
19848
20149
  return unwrap(await this.svc.deleteEnvironmentVariable(uuid2, key));
19849
20150
  }
20151
+ async syncEnv(uuid2, options = {}) {
20152
+ const { filePath, dryRun = false, prune = false, onProgress } = options;
20153
+ let envContent;
20154
+ const { readFileSync } = await import("node:fs");
20155
+ const { resolve, isAbsolute } = await import("node:path");
20156
+ const target = filePath || ".env";
20157
+ const absoluteTarget = isAbsolute(target) ? target : resolve(process.cwd(), target);
20158
+ try {
20159
+ envContent = readFileSync(absoluteTarget, "utf-8");
20160
+ } catch (err2) {
20161
+ const msg = err2 instanceof Error ? err2.message : String(err2);
20162
+ throw new Error(`Cannot read env file at ${absoluteTarget} (resolved from ${target}): ${msg}`);
20163
+ }
20164
+ const localVars = this.parseEnvContent(envContent);
20165
+ if (localVars.size === 0) {
20166
+ return { added: [], updated: [], removed: [], skipped: 0 };
20167
+ }
20168
+ const currentVarsList = await this.envVars(uuid2);
20169
+ const currentVars = new Map(currentVarsList.map((v) => [v.key, v.value]));
20170
+ const toAdd = [];
20171
+ const toUpdate = [];
20172
+ const toRemove = [];
20173
+ for (const [key, value] of localVars.entries()) {
20174
+ const currentValue = currentVars.get(key);
20175
+ if (!currentValue) {
20176
+ toAdd.push({ key, value });
20177
+ } else if (currentValue !== value) {
20178
+ toUpdate.push({ key, value, oldValue: currentValue });
20179
+ }
20180
+ }
20181
+ if (prune) {
20182
+ for (const key of currentVars.keys()) {
20183
+ if (!localVars.has(key)) {
20184
+ toRemove.push(key);
20185
+ }
20186
+ }
20187
+ }
20188
+ if (!dryRun) {
20189
+ for (const { key, value } of toAdd) {
20190
+ await this.setEnv(uuid2, key, value, false);
20191
+ onProgress?.({ type: "add", key, value });
20192
+ }
20193
+ for (const { key, value } of toUpdate) {
20194
+ await this.setEnv(uuid2, key, value, false);
20195
+ onProgress?.({ type: "update", key, value });
20196
+ }
20197
+ for (const key of toRemove) {
20198
+ await this.deleteEnv(uuid2, key);
20199
+ onProgress?.({ type: "remove", key });
20200
+ }
20201
+ } else {
20202
+ for (const { key, value } of toAdd) {
20203
+ onProgress?.({ type: "add", key, value });
20204
+ }
20205
+ for (const { key, value } of toUpdate) {
20206
+ onProgress?.({ type: "update", key, value });
20207
+ }
20208
+ for (const key of toRemove) {
20209
+ onProgress?.({ type: "remove", key });
20210
+ }
20211
+ }
20212
+ return {
20213
+ added: toAdd,
20214
+ updated: toUpdate,
20215
+ removed: toRemove,
20216
+ skipped: currentVars.size - toUpdate.length - toRemove.length
20217
+ };
20218
+ }
20219
+ parseEnvContent(content) {
20220
+ return parseEnvContent(content);
20221
+ }
19850
20222
  }
19851
20223
 
19852
20224
  class DatabasesResource {
@@ -19893,6 +20265,15 @@ class DatabasesResource {
19893
20265
  async deleteBackup(dbUuid, backupUuid) {
19894
20266
  return unwrap(await this.svc.deleteDatabaseBackup(dbUuid, backupUuid));
19895
20267
  }
20268
+ async envVars(uuid2) {
20269
+ return unwrap(await this.svc.listDatabaseEnvVars(uuid2));
20270
+ }
20271
+ async bulkSetEnv(uuid2, vars) {
20272
+ return unwrap(await this.svc.bulkUpdateDatabaseEnvVars(uuid2, vars));
20273
+ }
20274
+ async deleteEnv(uuid2, key) {
20275
+ return unwrap(await this.svc.deleteDatabaseEnvVar(uuid2, key));
20276
+ }
19896
20277
  }
19897
20278
 
19898
20279
  class ServicesResource {
@@ -19931,7 +20312,13 @@ class ServicesResource {
19931
20312
  return unwrap(await this.svc.listServiceEnvVars(uuid2));
19932
20313
  }
19933
20314
  async setEnv(uuid2, data) {
19934
- return unwrap(await this.svc.createServiceEnvVar(uuid2, data));
20315
+ return unwrap(await this.svc.bulkUpdateServiceEnvVars(uuid2, [data]));
20316
+ }
20317
+ async bulkSetEnv(uuid2, vars) {
20318
+ return unwrap(await this.svc.bulkUpdateServiceEnvVars(uuid2, vars));
20319
+ }
20320
+ async deleteEnv(uuid2, key) {
20321
+ return unwrap(await this.svc.deleteServiceEnvVar(uuid2, key));
19935
20322
  }
19936
20323
  }
19937
20324
 
@@ -20265,11 +20652,26 @@ async function handleToolCall(name, args) {
20265
20652
  startCommand: a.startCommand,
20266
20653
  domains: a.domains,
20267
20654
  isForceHttpsEnabled: a.isForceHttpsEnabled,
20268
- isAutoDeployEnabled: a.isAutoDeployEnabled
20655
+ isAutoDeployEnabled: a.isAutoDeployEnabled,
20656
+ watchPaths: a.watchPaths
20269
20657
  }).then((app) => ({
20270
20658
  message: `Application ${a.uuid} updated`,
20271
20659
  application: app
20272
20660
  })), "Failed to update application");
20661
+ case "get_application":
20662
+ return mcpCall((s) => s.applications.get(a.uuid).then((app) => ({
20663
+ uuid: app.uuid,
20664
+ name: app.name,
20665
+ status: app.status,
20666
+ fqdn: app.fqdn,
20667
+ git_repository: app.git_repository,
20668
+ git_branch: app.git_branch,
20669
+ build_pack: app.build_pack,
20670
+ dockerfile_location: app.dockerfile_location,
20671
+ base_directory: app.base_directory,
20672
+ watch_paths: app.watch_paths,
20673
+ settings: app.settings
20674
+ })), "Failed to get application");
20273
20675
  case "get_application_logs":
20274
20676
  return mcpCall((s) => s.applications.logs(a.uuid, { tail: a.tail, serviceName: a.serviceName }).then((logs) => ({
20275
20677
  timestamp: logs.timestamp,