@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
@@ -86,6 +86,184 @@ export function formatBytes(bytes: number): string {
86
86
  return `${bytes.toFixed(1)} ${units[i]}`;
87
87
  }
88
88
 
89
+ // ─── Validation ─────────────────────────────────────────────────────────────
90
+
91
+ /**
92
+ * Validates a port number string (single or comma-separated).
93
+ * Valid: "3000", "3000,3001", "80,443,8080"
94
+ * Invalid: "abc", "99999", "0", "-1", "3000,", ",3000"
95
+ *
96
+ * @param ports - Port string to validate
97
+ * @returns Object with valid flag, parsed ports, and error message
98
+ */
99
+ export function validatePorts(ports: string): {
100
+ valid: boolean;
101
+ ports: number[];
102
+ error?: string;
103
+ } {
104
+ if (!ports || !ports.trim()) {
105
+ return { valid: false, ports: [], error: "Port string is empty" };
106
+ }
107
+
108
+ const parts = ports.split(",").map((p) => p.trim());
109
+ const parsed: number[] = [];
110
+
111
+ for (const part of parts) {
112
+ if (!/^\d+$/.test(part)) {
113
+ return {
114
+ valid: false,
115
+ ports: [],
116
+ error: `Invalid port "${part}" — must be a number`,
117
+ };
118
+ }
119
+ const num = parseInt(part, 10);
120
+ if (num < 1 || num > 65535) {
121
+ return {
122
+ valid: false,
123
+ ports: [],
124
+ error: `Port ${num} out of range (1-65535)`,
125
+ };
126
+ }
127
+ parsed.push(num);
128
+ }
129
+
130
+ const dupes = parsed.filter((p, i) => parsed.indexOf(p) !== i);
131
+ if (dupes.length > 0) {
132
+ return {
133
+ valid: false,
134
+ ports: parsed,
135
+ error: `Duplicate port: ${dupes[0]}`,
136
+ };
137
+ }
138
+
139
+ return { valid: true, ports: parsed };
140
+ }
141
+
142
+ /**
143
+ * Parses EXPOSE directives from a Dockerfile.
144
+ *
145
+ * @param dockerfilePath - Path to the Dockerfile
146
+ * @returns Array of exposed port numbers, empty if none found or file unreadable
147
+ */
148
+ export function parseDockerfileExpose(dockerfilePath: string): number[] {
149
+ try {
150
+ const content = require("node:fs").readFileSync(dockerfilePath, "utf-8");
151
+ const ports: number[] = [];
152
+ for (const line of content.split("\n")) {
153
+ const match = line.match(/^\s*EXPOSE\s+(.+)/i);
154
+ if (match) {
155
+ for (const token of match[1].split(/\s+/)) {
156
+ // Handle "3000/tcp", "3000/udp", or just "3000"
157
+ const portStr = token.split("/")[0].trim();
158
+ const num = parseInt(portStr, 10);
159
+ if (!isNaN(num) && num >= 1 && num <= 65535) {
160
+ ports.push(num);
161
+ }
162
+ }
163
+ }
164
+ }
165
+ return [...new Set(ports)];
166
+ } catch {
167
+ return [];
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Validates a .coolify.json state object (single-app format).
173
+ * Checks required fields, types, and port validity.
174
+ *
175
+ * @param state - The parsed JSON object
176
+ * @returns Object with valid flag, warnings, and errors
177
+ */
178
+ export function validateCoolifyState(state: Record<string, unknown>): {
179
+ valid: boolean;
180
+ errors: string[];
181
+ warnings: string[];
182
+ } {
183
+ const errors: string[] = [];
184
+ const warnings: string[] = [];
185
+
186
+ // Detect format
187
+ const isMultiApp = Array.isArray(state.apps);
188
+ const isSingleApp = typeof state.appUuid === "string";
189
+
190
+ if (!isMultiApp && !isSingleApp) {
191
+ errors.push(
192
+ 'Invalid format: must have "appUuid" (single-app) or "apps" array (multi-app)',
193
+ );
194
+ return { valid: false, errors, warnings };
195
+ }
196
+
197
+ // Common required fields
198
+ const commonRequired = ["serverUuid", "projectUuid", "environmentUuid"];
199
+ for (const field of commonRequired) {
200
+ if (!state[field] || typeof state[field] !== "string") {
201
+ errors.push(`Missing or invalid required field: ${field}`);
202
+ }
203
+ }
204
+
205
+ if (isSingleApp) {
206
+ // Validate single-app specific fields
207
+ if (
208
+ state.portsExposes &&
209
+ typeof state.portsExposes === "string"
210
+ ) {
211
+ const portResult = validatePorts(state.portsExposes as string);
212
+ if (!portResult.valid) {
213
+ warnings.push(`portsExposes: ${portResult.error}`);
214
+ }
215
+ }
216
+
217
+ if (state.buildPack && typeof state.buildPack === "string") {
218
+ const validBuildPacks = [
219
+ "dockerfile",
220
+ "nixpacks",
221
+ "static",
222
+ "dockercompose",
223
+ ];
224
+ if (!validBuildPacks.includes(state.buildPack as string)) {
225
+ warnings.push(
226
+ `Unknown buildPack "${state.buildPack}" — expected: ${validBuildPacks.join(", ")}`,
227
+ );
228
+ }
229
+ }
230
+ }
231
+
232
+ if (isMultiApp) {
233
+ const apps = state.apps as Array<Record<string, unknown>>;
234
+ if (apps.length === 0) {
235
+ errors.push("Multi-app format requires at least one app");
236
+ }
237
+ for (let i = 0; i < apps.length; i++) {
238
+ const app = apps[i];
239
+ if (!app.uuid || typeof app.uuid !== "string") {
240
+ errors.push(`apps[${i}]: missing or invalid "uuid"`);
241
+ }
242
+ if (!app.name || typeof app.name !== "string") {
243
+ errors.push(`apps[${i}]: missing or invalid "name"`);
244
+ }
245
+ if (!app.service || typeof app.service !== "string") {
246
+ errors.push(`apps[${i}]: missing or invalid "service"`);
247
+ }
248
+ if (app.port !== undefined) {
249
+ const port = app.port as number;
250
+ if (typeof port !== "number" || port < 1 || port > 65535) {
251
+ warnings.push(`apps[${i}] (${app.name}): invalid port ${port}`);
252
+ }
253
+ }
254
+ }
255
+
256
+ // Check for duplicate UUIDs
257
+ const uuids = apps.map((a) => a.uuid).filter(Boolean);
258
+ const dupeUuids = uuids.filter((u, i) => uuids.indexOf(u) !== i);
259
+ if (dupeUuids.length > 0) {
260
+ errors.push(`Duplicate app UUIDs: ${dupeUuids.join(", ")}`);
261
+ }
262
+ }
263
+
264
+ return { valid: errors.length === 0, errors, warnings };
265
+ }
266
+
89
267
  /**
90
268
  * Formats timestamp to relative time.
91
269
  *