@mks2508/coolify-mks-cli-mcp 0.8.0 → 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 (45) hide show
  1. package/dist/cli/coolify-state.d.ts +12 -4
  2. package/dist/cli/coolify-state.d.ts.map +1 -1
  3. package/dist/cli/index.js +8886 -7957
  4. package/dist/coolify/config.d.ts +25 -0
  5. package/dist/coolify/config.d.ts.map +1 -1
  6. package/dist/coolify/index.d.ts +118 -10
  7. package/dist/coolify/index.d.ts.map +1 -1
  8. package/dist/coolify/types.d.ts +61 -1
  9. package/dist/coolify/types.d.ts.map +1 -1
  10. package/dist/index.cjs +2267 -227
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.js +2289 -227
  13. package/dist/index.js.map +1 -1
  14. package/dist/sdk.d.ts +56 -8
  15. package/dist/sdk.d.ts.map +1 -1
  16. package/dist/server/stdio.js +253 -100
  17. package/dist/tools/definitions.d.ts.map +1 -1
  18. package/dist/tools/handlers.d.ts.map +1 -1
  19. package/dist/utils/env-parser.d.ts +24 -0
  20. package/dist/utils/env-parser.d.ts.map +1 -0
  21. package/dist/utils/format.d.ts +32 -0
  22. package/dist/utils/format.d.ts.map +1 -1
  23. package/package.json +2 -1
  24. package/src/cli/commands/create.ts +279 -37
  25. package/src/cli/commands/env.ts +348 -54
  26. package/src/cli/commands/init.ts +69 -15
  27. package/src/cli/commands/main-menu.ts +1 -1
  28. package/src/cli/commands/projects.ts +3 -3
  29. package/src/cli/commands/show.ts +39 -10
  30. package/src/cli/commands/status.ts +23 -7
  31. package/src/cli/commands/svc.ts +7 -1
  32. package/src/cli/commands/update.ts +52 -0
  33. package/src/cli/commands/volumes.ts +293 -0
  34. package/src/cli/coolify-state.ts +42 -4
  35. package/src/cli/index.ts +50 -4
  36. package/src/cli/ui/banner.ts +3 -3
  37. package/src/cli/ui/screen.ts +26 -2
  38. package/src/coolify/config.ts +75 -0
  39. package/src/coolify/index.ts +325 -106
  40. package/src/coolify/types.ts +62 -1
  41. package/src/sdk.ts +87 -39
  42. package/src/tools/definitions.ts +22 -0
  43. package/src/tools/handlers.ts +19 -0
  44. package/src/utils/env-parser.ts +45 -0
  45. 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
  };
18596
18647
  if (options.githubRepoUrl) {
18597
- body.git_repository = options.githubRepoUrl.replace(/^https?:\/\/github\.com\//, "").replace(/\.git$/, "");
18648
+ body.git_repository = options.githubRepoUrl;
18598
18649
  }
18599
18650
  if (appType === "public" || appType === "private-github-app" || appType === "private-deploy-key") {
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, is_buildtime: 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, is_buildtime: 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) {
@@ -18683,39 +18720,16 @@ class CoolifyService {
18683
18720
  }
18684
18721
  async setEnvironmentVariable(appUuid, key, value, isBuildTime = false) {
18685
18722
  log.info(`Setting environment variable ${key} for ${appUuid} (buildtime: ${isBuildTime})`);
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({
18696
- key,
18697
- value,
18698
- is_buildtime: isBuildTime
18699
- })
18700
- });
18701
- if (result.error) {
18702
- log.error(`Failed to update env var: ${result.error}`);
18703
- return err(new Error(result.error));
18704
- }
18705
- } else {
18706
- log.debug(`Variable ${key} does not exist, using POST to create`);
18707
- const result = await this.request(`/applications/${appUuid}/envs`, {
18708
- method: "POST",
18709
- body: JSON.stringify({
18710
- key,
18711
- value,
18712
- is_buildtime: isBuildTime
18713
- })
18714
- });
18715
- if (result.error) {
18716
- log.error(`Failed to create env var: ${result.error}`);
18717
- return err(new Error(result.error));
18723
+ const result = await this.bulkUpdateEnvironmentVariables(appUuid, [
18724
+ {
18725
+ key,
18726
+ value,
18727
+ is_buildtime: isBuildTime,
18728
+ is_runtime: !isBuildTime
18718
18729
  }
18730
+ ]);
18731
+ if (isErr(result)) {
18732
+ return err(result.error);
18719
18733
  }
18720
18734
  log.success(`Environment variable ${key} set for ${appUuid}`);
18721
18735
  return ok(undefined);
@@ -18789,6 +18803,23 @@ class CoolifyService {
18789
18803
  return err(new Error(result.error));
18790
18804
  return ok(result.data || []);
18791
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
+ }
18792
18823
  async listProjects(page, perPage) {
18793
18824
  let endpoint = "/projects";
18794
18825
  const params = new URLSearchParams;
@@ -18924,10 +18955,14 @@ class CoolifyService {
18924
18955
  body.domains = options.domains;
18925
18956
  if (options.dockerComposeDomains)
18926
18957
  body.docker_compose_domains = options.dockerComposeDomains;
18958
+ if (options.dockerComposeRaw !== undefined)
18959
+ body.docker_compose_raw = options.dockerComposeRaw;
18927
18960
  if (options.isForceHttpsEnabled !== undefined)
18928
18961
  body.is_force_https_enabled = options.isForceHttpsEnabled;
18929
18962
  if (options.isAutoDeployEnabled !== undefined)
18930
18963
  body.is_auto_deploy_enabled = options.isAutoDeployEnabled;
18964
+ if (options.watchPaths !== undefined)
18965
+ body.watch_paths = options.watchPaths;
18931
18966
  if (options.healthCheckEnabled !== undefined)
18932
18967
  body.health_check_enabled = options.healthCheckEnabled;
18933
18968
  if (options.healthCheckPath)
@@ -18998,7 +19033,7 @@ class CoolifyService {
18998
19033
  log.info(`Bulk updating ${envVars.length} env vars for ${appUuid}`);
18999
19034
  const result = await this.request(`/applications/${appUuid}/envs/bulk`, {
19000
19035
  method: "PATCH",
19001
- body: JSON.stringify(envVars)
19036
+ body: JSON.stringify({ data: envVars })
19002
19037
  });
19003
19038
  if (result.error) {
19004
19039
  log.error(`Failed to bulk update env vars: ${result.error}`);
@@ -19007,6 +19042,19 @@ class CoolifyService {
19007
19042
  log.success(`Bulk updated ${envVars.length} env vars for ${appUuid}`);
19008
19043
  return ok(result.data || { message: "Environment variables updated" });
19009
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
+ }
19010
19058
  async getApplicationDeploymentHistory(appUuid) {
19011
19059
  log.info(`Getting deployment history for ${appUuid}`);
19012
19060
  const result = await this.request(`/deployments/applications/${appUuid}`);
@@ -19426,12 +19474,76 @@ class CoolifyService {
19426
19474
  return err(new Error(result.error));
19427
19475
  return ok(result.data || []);
19428
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
+ }
19429
19490
  async createServiceEnvVar(uuid2, data) {
19430
19491
  const result = await this.request(`/services/${uuid2}/envs`, { method: "POST", body: JSON.stringify(data) });
19431
19492
  if (result.error)
19432
19493
  return err(new Error(result.error));
19433
19494
  return ok(result.data);
19434
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
+ }
19435
19547
  async getServerResources(serverUuid) {
19436
19548
  log.info(`Getting resources for server ${serverUuid}`);
19437
19549
  const result = await this.request(`/servers/${serverUuid}/resources`);
@@ -19948,6 +20060,28 @@ Please use the full UUID.`));
19948
20060
  }
19949
20061
  }
19950
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
+
19951
20085
  // src/sdk.ts
19952
20086
  function unwrap(result) {
19953
20087
  if (isErr(result))
@@ -19966,6 +20100,9 @@ class ApplicationsResource {
19966
20100
  async listSummaries() {
19967
20101
  return unwrap(await this.svc.listApplicationSummaries());
19968
20102
  }
20103
+ async get(uuid2) {
20104
+ return unwrap(await this.svc.getApplication(uuid2));
20105
+ }
19969
20106
  async resolve(query) {
19970
20107
  return unwrap(await this.svc.resolveApplication(query));
19971
20108
  }
@@ -20014,10 +20151,15 @@ class ApplicationsResource {
20014
20151
  async syncEnv(uuid2, options = {}) {
20015
20152
  const { filePath, dryRun = false, prune = false, onProgress } = options;
20016
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);
20017
20158
  try {
20018
- envContent = await Bun.file(filePath || ".env").text();
20019
- } catch {
20020
- throw new Error(filePath ? `Cannot read file: ${filePath}` : "No .env file found in current directory");
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}`);
20021
20163
  }
20022
20164
  const localVars = this.parseEnvContent(envContent);
20023
20165
  if (localVars.size === 0) {
@@ -20075,26 +20217,7 @@ class ApplicationsResource {
20075
20217
  };
20076
20218
  }
20077
20219
  parseEnvContent(content) {
20078
- const envVars = new Map;
20079
- const lines = content.split(`
20080
- `);
20081
- for (const line of lines) {
20082
- const trimmedLine = line.trim();
20083
- if (!trimmedLine || trimmedLine.startsWith("#")) {
20084
- continue;
20085
- }
20086
- const eqIndex = trimmedLine.indexOf("=");
20087
- if (eqIndex === -1) {
20088
- continue;
20089
- }
20090
- const key = trimmedLine.slice(0, eqIndex).trim();
20091
- let value = trimmedLine.slice(eqIndex + 1).trim();
20092
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
20093
- value = value.slice(1, -1);
20094
- }
20095
- envVars.set(key, value);
20096
- }
20097
- return envVars;
20220
+ return parseEnvContent(content);
20098
20221
  }
20099
20222
  }
20100
20223
 
@@ -20142,6 +20265,15 @@ class DatabasesResource {
20142
20265
  async deleteBackup(dbUuid, backupUuid) {
20143
20266
  return unwrap(await this.svc.deleteDatabaseBackup(dbUuid, backupUuid));
20144
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
+ }
20145
20277
  }
20146
20278
 
20147
20279
  class ServicesResource {
@@ -20180,7 +20312,13 @@ class ServicesResource {
20180
20312
  return unwrap(await this.svc.listServiceEnvVars(uuid2));
20181
20313
  }
20182
20314
  async setEnv(uuid2, data) {
20183
- 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));
20184
20322
  }
20185
20323
  }
20186
20324
 
@@ -20514,11 +20652,26 @@ async function handleToolCall(name, args) {
20514
20652
  startCommand: a.startCommand,
20515
20653
  domains: a.domains,
20516
20654
  isForceHttpsEnabled: a.isForceHttpsEnabled,
20517
- isAutoDeployEnabled: a.isAutoDeployEnabled
20655
+ isAutoDeployEnabled: a.isAutoDeployEnabled,
20656
+ watchPaths: a.watchPaths
20518
20657
  }).then((app) => ({
20519
20658
  message: `Application ${a.uuid} updated`,
20520
20659
  application: app
20521
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");
20522
20675
  case "get_application_logs":
20523
20676
  return mcpCall((s) => s.applications.logs(a.uuid, { tail: a.tail, serviceName: a.serviceName }).then((logs) => ({
20524
20677
  timestamp: logs.timestamp,
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/tools/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,IAAI,EAmqC9B,CAAC"}
1
+ {"version":3,"file":"definitions.d.ts","sourceRoot":"","sources":["../../src/tools/definitions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAE/D;;GAEG;AACH,eAAO,MAAM,YAAY,EAAE,IAAI,EAyrC9B,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/tools/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AA+DzE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC,CAgfzB"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/tools/handlers.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAC;AA+DzE;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,cAAc,CAAC,CAmgBzB"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * .env file parser shared between the SDK (syncEnv) and the CLI (--sync stdin).
3
+ *
4
+ * Kept as a standalone exported function (not a class method) so both the
5
+ * SDK's ApplicationsResource and the CLI's stdin sync handler can use the
6
+ * same implementation without coupling to either.
7
+ *
8
+ * @module
9
+ */
10
+ /**
11
+ * Parses .env-style content into a Map.
12
+ *
13
+ * Handles:
14
+ * - Comments (lines starting with `#`)
15
+ * - Empty lines (skipped)
16
+ * - Quoted values (`"value"` or `'value'`)
17
+ * - Values containing `=` (only the first `=` is the separator)
18
+ * - Lines without `=` (skipped — invalid)
19
+ *
20
+ * @param content - File contents in KEY=VALUE format
21
+ * @returns Map of key → value (empty string for `KEY=` with no value)
22
+ */
23
+ export declare function parseEnvContent(content: string): Map<string, string>;
24
+ //# sourceMappingURL=env-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"env-parser.d.ts","sourceRoot":"","sources":["../../src/utils/env-parser.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH;;;;;;;;;;;;GAYG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAqBpE"}
@@ -28,6 +28,38 @@ export declare function formatStatus(status: string): string;
28
28
  * @returns Formatted string with unit
29
29
  */
30
30
  export declare function formatBytes(bytes: number): string;
31
+ /**
32
+ * Validates a port number string (single or comma-separated).
33
+ * Valid: "3000", "3000,3001", "80,443,8080"
34
+ * Invalid: "abc", "99999", "0", "-1", "3000,", ",3000"
35
+ *
36
+ * @param ports - Port string to validate
37
+ * @returns Object with valid flag, parsed ports, and error message
38
+ */
39
+ export declare function validatePorts(ports: string): {
40
+ valid: boolean;
41
+ ports: number[];
42
+ error?: string;
43
+ };
44
+ /**
45
+ * Parses EXPOSE directives from a Dockerfile.
46
+ *
47
+ * @param dockerfilePath - Path to the Dockerfile
48
+ * @returns Array of exposed port numbers, empty if none found or file unreadable
49
+ */
50
+ export declare function parseDockerfileExpose(dockerfilePath: string): number[];
51
+ /**
52
+ * Validates a .coolify.json state object (single-app format).
53
+ * Checks required fields, types, and port validity.
54
+ *
55
+ * @param state - The parsed JSON object
56
+ * @returns Object with valid flag, warnings, and errors
57
+ */
58
+ export declare function validateCoolifyState(state: Record<string, unknown>): {
59
+ valid: boolean;
60
+ errors: string[];
61
+ warnings: string[];
62
+ };
31
63
  /**
32
64
  * Formats timestamp to relative time.
33
65
  *
@@ -1 +1 @@
1
- {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAY/B;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,eAOlE;AAmBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWnD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAY5D"}
1
+ {"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/utils/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,MAAM,YAAY,CAAC;AAY/B;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,eAOlE;AAmBD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAWnD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQjD;AAID;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAC5C,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAqCA;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,EAAE,CAqBtE;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACpE,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAmFA;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAY5D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mks2508/coolify-mks-cli-mcp",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "MCP server and CLI for Coolify deployment management",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -66,6 +66,7 @@
66
66
  "shiki": "^4.0.2"
67
67
  },
68
68
  "devDependencies": {
69
+ "@types/bun": "^1.3.14",
69
70
  "@types/node": "^22.10.5",
70
71
  "@types/prompts": "^2.4.9",
71
72
  "rolldown": "^1.0.0-beta.58",