@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
package/src/cli/index.ts CHANGED
@@ -82,6 +82,11 @@ import {
82
82
  networkInspectCommand,
83
83
  analyzeDeployCommand,
84
84
  } from "./commands/network.js";
85
+ import {
86
+ volumesListCommand,
87
+ volumesAddCommand,
88
+ volumesRemoveCommand,
89
+ } from "./commands/volumes.js";
85
90
 
86
91
  const program = new Command();
87
92
 
@@ -94,11 +99,11 @@ program
94
99
  `${chalk.bold.hex("#8c52ff")("Coolify CLI")} — Manage deployments, env vars, and resources\n` +
95
100
  ` Global: ${chalk.gray("bun install -g @mks2508/coolify-mks-cli-mcp")}`,
96
101
  )
97
- .version("0.8.0")
102
+ .version("0.9.0")
98
103
  .addHelpText("beforeAll", () => {
99
104
  // Show banner before help output
100
105
  const { showAutoBanner } = require("./ui/banner.js");
101
- showAutoBanner("0.8.0");
106
+ showAutoBanner("0.9.0");
102
107
  return "";
103
108
  });
104
109
 
@@ -149,6 +154,10 @@ program
149
154
  "--github-app-uuid <uuid>",
150
155
  "GitHub App UUID (auto-detected if not provided)",
151
156
  )
157
+ .option(
158
+ "--private-key-uuid <uuid>",
159
+ "Private key UUID (required for private-deploy-key type)",
160
+ )
152
161
  .option(
153
162
  "--domain <domain>",
154
163
  "Domain to set after creation (e.g., app.example.com or https://app.example.com)",
@@ -247,14 +256,21 @@ program
247
256
  program
248
257
  .command("env [uuid]")
249
258
  .description("Manage env vars (reads .coolify.json if no UUID)")
250
- .option("--set <KEY=VALUE>", "Set an environment variable")
259
+ .option(
260
+ "--set <KEY=VALUE>",
261
+ "Set an environment variable (can be repeated: --set A=1 --set B=2)",
262
+ (value: string, prev: string[] | undefined) => (prev ?? []).concat(value),
263
+ [] as string[],
264
+ )
251
265
  .option("--delete <KEY>", "Delete an environment variable")
266
+ .option("--get <KEY>", "Get a single environment variable value (prints value, no colors)")
252
267
  .option("--buildtime", "Mark variable as build-time only (use with --set)")
253
268
  .option("--runtime-only", "Mark variable as runtime only, not build-time (use with --set)")
254
- .option("--sync [file]", "Sync env vars from .env file (default: .env)")
269
+ .option("--sync [file]", "Sync env vars from .env file (default: .env, or stdin with -)")
255
270
  .option("--dry-run", "Preview changes without applying (use with --sync)")
256
271
  .option("--prune", "Delete vars not in file (use with --sync)")
257
272
  .option("--table", "Show env vars in table format")
273
+ .option("--json", "Output machine-parseable JSON (ICoolifyEnvVar[])")
258
274
  .action((uuid, options) => envCommand(uuid, options));
259
275
 
260
276
  // Update application
@@ -283,6 +299,11 @@ program
283
299
  )
284
300
  .option("--auto-deploy", "Enable auto-deploy on git push")
285
301
  .option("--no-auto-deploy", "Disable auto-deploy on git push")
302
+ .option(
303
+ "--watch-paths <paths>",
304
+ 'Watch paths for selective auto-deploy (newline-separated globs, e.g. "src/**\\npackages/**")',
305
+ )
306
+ .option("--clear-watch-paths", "Clear watch paths (deploy on all changes)")
286
307
  .option("--force-https", "Enable forced HTTPS redirect")
287
308
  .option("--health-check-enabled", "Enable health check")
288
309
  .option("--no-health-check-enabled", "Disable health check")
@@ -553,6 +574,31 @@ program
553
574
  .description("Analyze a failed deployment (extract errors, suggest fixes)")
554
575
  .action(analyzeDeployCommand);
555
576
 
577
+ // ─── Volumes subcommands ──────────────────────────────────────────────────────
578
+
579
+ const volumes = program.command("volumes").description("Manage volumes for docker-compose apps");
580
+ volumes
581
+ .command("list <uuid>")
582
+ .description("List volumes for an application")
583
+ .option("--service <name>", "Service name (for multi-service compose)")
584
+ .action((uuid, options) => volumesListCommand(uuid, options));
585
+ volumes
586
+ .command("add <uuid>")
587
+ .description("Add a bind mount volume")
588
+ .requiredOption("--source <path>", "Source path on host")
589
+ .requiredOption("--target <path>", "Target path in container")
590
+ .option("--service <name>", "Service name (required for multi-service compose)")
591
+ .option("--no-restart", "Skip automatic redeploy after adding volume")
592
+ .action((uuid, options) => volumesAddCommand(uuid, options));
593
+ volumes
594
+ .command("remove <uuid>")
595
+ .description("Remove a volume by target path")
596
+ .requiredOption("--target <path>", "Target path in container to remove")
597
+ .option("--service <name>", "Service name (required for multi-service compose)")
598
+ .option("--no-restart", "Skip automatic redeploy after removing volume")
599
+ .action((uuid, options) => volumesRemoveCommand(uuid, options));
600
+ volumes.action(() => volumes.help());
601
+
556
602
  // Show help by default (or interactive menu if no args)
557
603
  program.action(async () => {
558
604
  // If called without arguments, show interactive menu
@@ -203,7 +203,7 @@ export function getMiniLogoLines(): string[] {
203
203
  * @param subtitle - Optional subtitle line
204
204
  */
205
205
  export function showBanner(
206
- version: string = "0.8.0",
206
+ version: string = "0.9.0",
207
207
  subtitle?: string,
208
208
  ): void {
209
209
  const logoLines = renderLogo();
@@ -244,7 +244,7 @@ export function showBanner(
244
244
  *
245
245
  * @param version - CLI version string
246
246
  */
247
- export function showCompactBanner(version: string = "0.8.0"): void {
247
+ export function showCompactBanner(version: string = "0.9.0"): void {
248
248
  console.log(
249
249
  `${chalk.hex("#8c52ff")("█")} ${chalk.bold("Coolify CLI")} ${chalk.gray(`v${version}`)}`,
250
250
  );
@@ -257,7 +257,7 @@ export function showCompactBanner(version: string = "0.8.0"): void {
257
257
  * @param subtitle - Optional subtitle for full banner
258
258
  */
259
259
  export function showAutoBanner(
260
- version: string = "0.8.0",
260
+ version: string = "0.9.0",
261
261
  subtitle?: string,
262
262
  ): void {
263
263
  if (process.stdout.isTTY) {
@@ -12,7 +12,7 @@ import type {
12
12
  ICoolifyApplication,
13
13
  ICoolifyResource,
14
14
  } from "../../coolify/types.js";
15
- import { loadConfig } from "../../coolify/config.js";
15
+ import { loadConfig, getCachedAppSettings } from "../../coolify/config.js";
16
16
  import { getMiniLogoLines } from "./banner.js";
17
17
 
18
18
  // ─── ANSI ────────────────────────────────────────────────────────────────────
@@ -147,7 +147,7 @@ function renderHeader(tree: ICoolifyInfrastructureTree): void {
147
147
 
148
148
  const textLines = [
149
149
  "",
150
- `${chalk.bold.hex("#a875ff")("Coolify CLI")} ${chalk.hex("#777777")("v0.8.0")}`,
150
+ `${chalk.bold.hex("#a875ff")("Coolify CLI")} ${chalk.hex("#777777")("v0.9.0")}`,
151
151
  "",
152
152
  `${chalk.hex("#cccccc")(serverDisplay)}`,
153
153
  "",
@@ -400,6 +400,25 @@ function buildDetailPanel(
400
400
  );
401
401
  }
402
402
 
403
+ // Deploy settings (auto-deploy from API settings or local cache, watch_paths from API)
404
+ lines.push("");
405
+ const autoDeployFromApi = app.settings?.is_auto_deploy_enabled;
406
+ // getCachedAppSettings is async but we need sync here — use a pre-fetched value
407
+ // passed via the context, or show API value / fallback
408
+ if (autoDeployFromApi !== undefined) {
409
+ lines.push(
410
+ `${chalk.hex("#888888")("Auto-deploy:")} ${autoDeployFromApi ? chalk.green("ON") : chalk.red("OFF")}`,
411
+ );
412
+ }
413
+ if (app.watch_paths) {
414
+ const paths = app.watch_paths.split("\n").filter(Boolean);
415
+ lines.push(`${chalk.hex("#888888")("Watch:")} ${chalk.hex("#cccccc")(paths[0])}`);
416
+ for (const p of paths.slice(1, 3)) {
417
+ lines.push(` ${chalk.hex("#cccccc")(p)}`);
418
+ }
419
+ if (paths.length > 3) lines.push(chalk.hex("#555555")(` +${paths.length - 3} more`));
420
+ }
421
+
403
422
  if (app.install_command || app.build_command || app.start_command) {
404
423
  lines.push("");
405
424
  if (app.install_command) lines.push(`${chalk.hex("#888888")("Install:")} ${chalk.hex("#aaaaaa")(app.install_command.slice(0, rightW - 10))}`);
@@ -564,6 +583,11 @@ export function buildResourcePreview(app: ICoolifyApplication): string[] {
564
583
  for (const [label, value] of fields) {
565
584
  if (value) lines.push(`${chalk.hex("#888888")(label + ":")} ${chalk.hex("#cccccc")(value)}`);
566
585
  }
586
+ if (app.watch_paths) {
587
+ const paths = app.watch_paths.split("\n").filter(Boolean);
588
+ lines.push(`${chalk.hex("#888888")("Watch:")} ${chalk.hex("#cccccc")(paths[0])}`);
589
+ if (paths.length > 1) lines.push(chalk.hex("#555555")(` +${paths.length - 1} more`));
590
+ }
567
591
  if (app.destination?.server) {
568
592
  lines.push("");
569
593
  lines.push(`${chalk.hex("#888888")("Server:")} ${chalk.hex("#cccccc")(app.destination.server.name)}`);
@@ -112,4 +112,79 @@ export async function getCoolifyToken(): Promise<string | undefined> {
112
112
  return process.env.COOLIFY_TOKEN;
113
113
  }
114
114
 
115
+ // ─── App Settings Cache ──────────────────────────────────────────────────────
116
+ // Coolify API GET does not return application_settings (is_auto_deploy_enabled,
117
+ // is_force_https_enabled, etc). We cache these locally when the user sets them
118
+ // via our CLI so we can display them later in `show`.
119
+
120
+ const SETTINGS_CACHE_FILE = join(CONFIG_DIR, "app-settings-cache.json");
121
+
122
+ /**
123
+ * Cached application settings that the Coolify API doesn't expose in GET responses.
124
+ */
125
+ export interface ICachedAppSettings {
126
+ /** Auto-deploy on git push */
127
+ isAutoDeployEnabled?: boolean;
128
+ /** Watch paths for selective deploy */
129
+ watchPaths?: string | null;
130
+ /** When this cache entry was last updated */
131
+ cachedAt?: string;
132
+ }
133
+
134
+ /**
135
+ * Reads the full settings cache from disk.
136
+ *
137
+ * @returns Map of appUuid → cached settings
138
+ */
139
+ async function readSettingsCache(): Promise<Record<string, ICachedAppSettings>> {
140
+ try {
141
+ if (existsSync(SETTINGS_CACHE_FILE)) {
142
+ const content = await readFile(SETTINGS_CACHE_FILE, "utf-8");
143
+ return JSON.parse(content);
144
+ }
145
+ } catch {
146
+ // Corrupted file — start fresh
147
+ }
148
+ return {};
149
+ }
150
+
151
+ /**
152
+ * Caches application settings locally after a successful PATCH.
153
+ *
154
+ * @param appUuid - Application UUID
155
+ * @param settings - Settings to cache (merged with existing)
156
+ */
157
+ export async function cacheAppSettings(
158
+ appUuid: string,
159
+ settings: Partial<ICachedAppSettings>,
160
+ ): Promise<void> {
161
+ try {
162
+ const cache = await readSettingsCache();
163
+ cache[appUuid] = {
164
+ ...cache[appUuid],
165
+ ...settings,
166
+ cachedAt: new Date().toISOString(),
167
+ };
168
+ if (!existsSync(CONFIG_DIR)) {
169
+ await mkdir(CONFIG_DIR, { recursive: true });
170
+ }
171
+ await writeFile(SETTINGS_CACHE_FILE, JSON.stringify(cache, null, 2));
172
+ } catch {
173
+ // Non-critical — silently ignore cache write failures
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Reads cached settings for an application.
179
+ *
180
+ * @param appUuid - Application UUID
181
+ * @returns Cached settings or null if not cached
182
+ */
183
+ export async function getCachedAppSettings(
184
+ appUuid: string,
185
+ ): Promise<ICachedAppSettings | null> {
186
+ const cache = await readSettingsCache();
187
+ return cache[appUuid] || null;
188
+ }
189
+
115
190
  export { CONFIG_DIR, CONFIG_FILE };