@mks2508/coolify-mks-cli-mcp 0.6.3 → 0.8.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.
- package/dist/cli/coolify-state.d.ts +92 -4
- package/dist/cli/coolify-state.d.ts.map +1 -1
- package/dist/cli/index.js +22149 -11456
- package/dist/cli/ui/highlighter.d.ts +28 -0
- package/dist/cli/ui/highlighter.d.ts.map +1 -0
- package/dist/cli/ui/index.d.ts +9 -0
- package/dist/cli/ui/index.d.ts.map +1 -0
- package/dist/cli/ui/spinners.d.ts +100 -0
- package/dist/cli/ui/spinners.d.ts.map +1 -0
- package/dist/cli/ui/tables.d.ts +103 -0
- package/dist/cli/ui/tables.d.ts.map +1 -0
- package/dist/coolify/index.d.ts +22 -3
- package/dist/coolify/index.d.ts.map +1 -1
- package/dist/coolify/types.d.ts +99 -1
- package/dist/coolify/types.d.ts.map +1 -1
- package/dist/examples/demo-ui.d.ts +8 -0
- package/dist/examples/demo-ui.d.ts.map +1 -0
- package/dist/index.cjs +322 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +322 -12
- package/dist/index.js.map +1 -1
- package/dist/sdk.d.ts +41 -0
- package/dist/sdk.d.ts.map +1 -1
- package/dist/server/stdio.js +258 -9
- package/package.json +16 -4
- package/src/cli/actions.ts +9 -2
- package/src/cli/commands/create.ts +71 -5
- package/src/cli/commands/db.ts +37 -0
- package/src/cli/commands/delete.ts +6 -2
- package/src/cli/commands/deploy.ts +347 -49
- package/src/cli/commands/deployments.ts +6 -2
- package/src/cli/commands/diagnose.ts +3 -3
- package/src/cli/commands/env.ts +121 -22
- package/src/cli/commands/exec.ts +6 -2
- package/src/cli/commands/init.ts +937 -0
- package/src/cli/commands/logs.ts +224 -24
- package/src/cli/commands/main-menu.ts +21 -0
- package/src/cli/commands/projects.ts +312 -29
- package/src/cli/commands/restart.ts +6 -2
- package/src/cli/commands/service-logs.ts +14 -0
- package/src/cli/commands/show.ts +6 -2
- package/src/cli/commands/start.ts +6 -2
- package/src/cli/commands/status.ts +538 -0
- package/src/cli/commands/stop.ts +6 -2
- package/src/cli/commands/update.ts +27 -2
- package/src/cli/coolify-state.ts +164 -11
- package/src/cli/index.ts +91 -10
- package/src/cli/name-resolver.ts +228 -0
- package/src/cli/ui/banner.ts +276 -0
- package/src/cli/ui/highlighter.ts +176 -0
- package/src/cli/ui/index.ts +9 -0
- package/src/cli/ui/prompts.ts +155 -0
- package/src/cli/ui/screen.ts +606 -0
- package/src/cli/ui/select.ts +280 -0
- package/src/cli/ui/spinners.ts +256 -0
- package/src/cli/ui/tables.ts +407 -0
- package/src/coolify/index.ts +257 -12
- package/src/coolify/types.ts +103 -1
- package/src/examples/demo-ui.ts +78 -0
- package/src/sdk.ts +162 -0
package/src/cli/coolify-state.ts
CHANGED
|
@@ -8,21 +8,32 @@
|
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
11
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* State stored in .coolify.json (generated by create-bunspace deployer).
|
|
15
|
+
* State stored in .coolify.json (generated by create-bunspace deployer or init command).
|
|
16
|
+
*
|
|
17
|
+
* This file links a local directory to a Coolify deployment, storing all necessary
|
|
18
|
+
* UUIDs and metadata for CLI commands to work without manual UUID entry.
|
|
16
19
|
*/
|
|
17
20
|
export interface ICoolifyDeployState {
|
|
18
21
|
/** Application UUID in Coolify */
|
|
19
22
|
appUuid: string;
|
|
23
|
+
/** Application name (for display purposes) */
|
|
24
|
+
appName?: string;
|
|
20
25
|
/** Server UUID */
|
|
21
26
|
serverUuid: string;
|
|
27
|
+
/** Server name (for display purposes) */
|
|
28
|
+
serverName?: string;
|
|
22
29
|
/** Project UUID */
|
|
23
30
|
projectUuid: string;
|
|
31
|
+
/** Project name (for display purposes) */
|
|
32
|
+
projectName?: string;
|
|
24
33
|
/** Environment UUID */
|
|
25
34
|
environmentUuid: string;
|
|
35
|
+
/** Environment name (for display purposes) */
|
|
36
|
+
environmentName?: string;
|
|
26
37
|
/** Domain if configured */
|
|
27
38
|
domain?: string;
|
|
28
39
|
/** Docker compose or Dockerfile path */
|
|
@@ -31,18 +42,72 @@ export interface ICoolifyDeployState {
|
|
|
31
42
|
baseDirectory?: string;
|
|
32
43
|
/** Git branch */
|
|
33
44
|
branch?: string;
|
|
34
|
-
/**
|
|
45
|
+
/** Git repository URL */
|
|
46
|
+
gitRepository?: string;
|
|
47
|
+
/** Application source type (github-app, deploy-key, docker-image, etc.) */
|
|
48
|
+
sourceType?: string;
|
|
49
|
+
/** Application type (legacy, may be undefined) */
|
|
35
50
|
type?: string;
|
|
36
|
-
/** Build pack */
|
|
51
|
+
/** Build pack (dockerfile, nixpacks, static, dockercompose) */
|
|
37
52
|
buildPack?: string;
|
|
38
53
|
/** Auto-deploy enabled */
|
|
39
54
|
autoDeployEnabled?: boolean;
|
|
55
|
+
/** When this state was last updated */
|
|
56
|
+
updatedAt?: string;
|
|
57
|
+
/** Coolify instance URL */
|
|
58
|
+
coolifyUrl?: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Multi-app state for monorepos deploying multiple services from one repo.
|
|
63
|
+
*
|
|
64
|
+
* Stored alongside or instead of the single-app state in .coolify.json.
|
|
65
|
+
*/
|
|
66
|
+
export interface ICoolifyMultiAppState {
|
|
67
|
+
/** All apps deployed from this repo */
|
|
68
|
+
apps: Array<{
|
|
69
|
+
/** Application UUID in Coolify */
|
|
70
|
+
uuid: string;
|
|
71
|
+
/** Application name */
|
|
72
|
+
name: string;
|
|
73
|
+
/** Service role (e.g. "backend", "admin", "docs", "proxy") */
|
|
74
|
+
service: string;
|
|
75
|
+
/** Domain if configured */
|
|
76
|
+
domain?: string;
|
|
77
|
+
/** Dockerfile path relative to repo root */
|
|
78
|
+
dockerfile?: string;
|
|
79
|
+
/** Exposed port */
|
|
80
|
+
port?: number;
|
|
81
|
+
}>;
|
|
82
|
+
/** Server UUID */
|
|
83
|
+
serverUuid: string;
|
|
84
|
+
/** Server name (for display purposes) */
|
|
85
|
+
serverName?: string;
|
|
86
|
+
/** Project UUID */
|
|
87
|
+
projectUuid: string;
|
|
88
|
+
/** Project name (for display purposes) */
|
|
89
|
+
projectName?: string;
|
|
90
|
+
/** Environment UUID */
|
|
91
|
+
environmentUuid: string;
|
|
92
|
+
/** Environment name (for display purposes) */
|
|
93
|
+
environmentName?: string;
|
|
94
|
+
/** Git branch */
|
|
95
|
+
branch?: string;
|
|
96
|
+
/** Git repository URL */
|
|
97
|
+
gitRepository?: string;
|
|
98
|
+
/** Coolify instance URL */
|
|
99
|
+
coolifyUrl?: string;
|
|
100
|
+
/** When this state was last updated */
|
|
101
|
+
updatedAt?: string;
|
|
40
102
|
}
|
|
41
103
|
|
|
42
104
|
const STATE_FILE = ".coolify.json";
|
|
43
105
|
|
|
44
106
|
/**
|
|
45
|
-
* Loads .coolify.json from the current working directory.
|
|
107
|
+
* Loads .coolify.json from the current working directory (single-app format).
|
|
108
|
+
*
|
|
109
|
+
* Handles both single-app and multi-app formats. For multi-app state with
|
|
110
|
+
* exactly one app, returns a synthesized single-app state for backward compat.
|
|
46
111
|
*
|
|
47
112
|
* @returns The deploy state if found, null otherwise
|
|
48
113
|
*/
|
|
@@ -55,21 +120,98 @@ export function loadCoolifyState(): ICoolifyDeployState | null {
|
|
|
55
120
|
|
|
56
121
|
try {
|
|
57
122
|
const content = readFileSync(statePath, "utf-8");
|
|
58
|
-
const state = JSON.parse(content)
|
|
123
|
+
const state = JSON.parse(content);
|
|
124
|
+
|
|
125
|
+
// Multi-app format — synthesize single-app state if exactly 1 app
|
|
126
|
+
if (Array.isArray(state.apps)) {
|
|
127
|
+
if (state.apps.length === 1) {
|
|
128
|
+
const app = state.apps[0];
|
|
129
|
+
return {
|
|
130
|
+
appUuid: app.uuid,
|
|
131
|
+
appName: app.name,
|
|
132
|
+
serverUuid: state.serverUuid,
|
|
133
|
+
serverName: state.serverName,
|
|
134
|
+
projectUuid: state.projectUuid,
|
|
135
|
+
projectName: state.projectName,
|
|
136
|
+
environmentUuid: state.environmentUuid,
|
|
137
|
+
environmentName: state.environmentName,
|
|
138
|
+
domain: app.domain,
|
|
139
|
+
branch: state.branch,
|
|
140
|
+
gitRepository: state.gitRepository,
|
|
141
|
+
coolifyUrl: state.coolifyUrl,
|
|
142
|
+
updatedAt: state.updatedAt,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
// Multi-app with multiple apps — cannot auto-select
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
59
148
|
|
|
149
|
+
// Single-app format
|
|
60
150
|
if (!state.appUuid) {
|
|
61
151
|
return null;
|
|
62
152
|
}
|
|
63
153
|
|
|
64
|
-
return state;
|
|
154
|
+
return state as ICoolifyDeployState;
|
|
65
155
|
} catch {
|
|
66
156
|
return null;
|
|
67
157
|
}
|
|
68
158
|
}
|
|
69
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Loads .coolify.json in multi-app format.
|
|
162
|
+
*
|
|
163
|
+
* If the file is in single-app format, returns null (use loadCoolifyState instead).
|
|
164
|
+
*
|
|
165
|
+
* @returns The multi-app state if found, null otherwise
|
|
166
|
+
*/
|
|
167
|
+
export function loadMultiAppState(): ICoolifyMultiAppState | null {
|
|
168
|
+
const statePath = join(process.cwd(), STATE_FILE);
|
|
169
|
+
|
|
170
|
+
if (!existsSync(statePath)) {
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
const content = readFileSync(statePath, "utf-8");
|
|
176
|
+
const state = JSON.parse(content);
|
|
177
|
+
|
|
178
|
+
if (!Array.isArray(state.apps)) {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return state as ICoolifyMultiAppState;
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Writes .coolify.json in multi-app format to the current working directory.
|
|
190
|
+
*
|
|
191
|
+
* @param state - The multi-app state to write
|
|
192
|
+
*/
|
|
193
|
+
export function writeMultiAppState(state: ICoolifyMultiAppState): void {
|
|
194
|
+
const statePath = join(process.cwd(), STATE_FILE);
|
|
195
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Writes .coolify.json to the current working directory.
|
|
200
|
+
*
|
|
201
|
+
* @param state - The state to write
|
|
202
|
+
*/
|
|
203
|
+
export function writeCoolifyState(state: ICoolifyDeployState): void {
|
|
204
|
+
const statePath = join(process.cwd(), STATE_FILE);
|
|
205
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
206
|
+
}
|
|
207
|
+
|
|
70
208
|
/**
|
|
71
209
|
* Resolves a UUID argument — if not provided, tries to read from .coolify.json.
|
|
72
210
|
*
|
|
211
|
+
* Supports both single-app and multi-app state formats. For multi-app state
|
|
212
|
+
* with exactly one app, auto-selects that app. For multiple apps, returns null
|
|
213
|
+
* (user must specify explicitly).
|
|
214
|
+
*
|
|
73
215
|
* @param uuid - UUID from CLI argument (may be undefined)
|
|
74
216
|
* @param field - Which field to read from .coolify.json (default: appUuid)
|
|
75
217
|
* @returns The resolved UUID or null if not found
|
|
@@ -78,11 +220,22 @@ export function resolveUuid(
|
|
|
78
220
|
uuid: string | undefined,
|
|
79
221
|
field: keyof ICoolifyDeployState = "appUuid",
|
|
80
222
|
): string | null {
|
|
81
|
-
if (
|
|
223
|
+
// Only return directly if it looks like a UUID (lowercase alphanumeric, 16-40 chars)
|
|
224
|
+
// Names like "mks-backend" should fall through to .coolify.json or name resolver
|
|
225
|
+
if (uuid && /^[a-z0-9]{16,40}$/.test(uuid)) return uuid;
|
|
82
226
|
|
|
227
|
+
// Try single-app state first (also handles multi-app with 1 app)
|
|
83
228
|
const state = loadCoolifyState();
|
|
84
|
-
if (
|
|
229
|
+
if (state) {
|
|
230
|
+
const value = state[field];
|
|
231
|
+
return typeof value === "string" ? value : null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Try multi-app state — only auto-resolve if exactly 1 app
|
|
235
|
+
const multiState = loadMultiAppState();
|
|
236
|
+
if (multiState && multiState.apps.length === 1 && field === "appUuid") {
|
|
237
|
+
return multiState.apps[0].uuid;
|
|
238
|
+
}
|
|
85
239
|
|
|
86
|
-
|
|
87
|
-
return typeof value === "string" ? value : null;
|
|
240
|
+
return null;
|
|
88
241
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -9,12 +9,17 @@
|
|
|
9
9
|
|
|
10
10
|
import { Command } from "commander";
|
|
11
11
|
import chalk from "chalk";
|
|
12
|
+
import { setVerbosity } from "@mks2508/better-logger";
|
|
13
|
+
|
|
14
|
+
// Silence SDK logger in CLI mode — all progress goes through spinners/prompts
|
|
15
|
+
setVerbosity("silent");
|
|
12
16
|
import { createCommand } from "./commands/create.js";
|
|
13
17
|
import { deployCommand } from "./commands/deploy.js";
|
|
14
18
|
import { listCommand } from "./commands/list.js";
|
|
15
19
|
import { logsCommand } from "./commands/logs.js";
|
|
16
20
|
import { serversCommand } from "./commands/servers.js";
|
|
17
21
|
import { projectsCommand } from "./commands/projects.js";
|
|
22
|
+
import { statusCommand } from "./commands/status.js";
|
|
18
23
|
import { environmentsCommand } from "./commands/environments.js";
|
|
19
24
|
import { configCommand } from "./commands/config.js";
|
|
20
25
|
import { envCommand } from "./commands/env.js";
|
|
@@ -28,6 +33,7 @@ import { stopCommand } from "./commands/stop.js";
|
|
|
28
33
|
import { restartCommand } from "./commands/restart.js";
|
|
29
34
|
import { buildLogsCommand } from "./commands/build-logs.js";
|
|
30
35
|
import { serviceLogsCommand } from "./commands/service-logs.js";
|
|
36
|
+
import { initCommand } from "./commands/init.js";
|
|
31
37
|
import { versionCommand } from "./commands/version.js";
|
|
32
38
|
import { databasesCommand } from "./commands/databases.js";
|
|
33
39
|
import { servicesCommand as servicesListCommand } from "./commands/services.js";
|
|
@@ -37,6 +43,7 @@ import {
|
|
|
37
43
|
dbListCommand,
|
|
38
44
|
dbGetCommand,
|
|
39
45
|
dbCreateCommand,
|
|
46
|
+
dbUpdateCommand,
|
|
40
47
|
dbStartCommand,
|
|
41
48
|
dbStopCommand,
|
|
42
49
|
dbRestartCommand,
|
|
@@ -78,10 +85,22 @@ import {
|
|
|
78
85
|
|
|
79
86
|
const program = new Command();
|
|
80
87
|
|
|
88
|
+
// Detect binary name to show correct help
|
|
89
|
+
const binaryName = process.argv[1]?.includes("coolify-cli") ? "coolify-cli" : "coolify-mcp";
|
|
90
|
+
|
|
81
91
|
program
|
|
82
|
-
.name(
|
|
83
|
-
.description(
|
|
84
|
-
|
|
92
|
+
.name(binaryName)
|
|
93
|
+
.description(
|
|
94
|
+
`${chalk.bold.hex("#8c52ff")("Coolify CLI")} — Manage deployments, env vars, and resources\n` +
|
|
95
|
+
` Global: ${chalk.gray("bun install -g @mks2508/coolify-mks-cli-mcp")}`,
|
|
96
|
+
)
|
|
97
|
+
.version("0.8.0")
|
|
98
|
+
.addHelpText("beforeAll", () => {
|
|
99
|
+
// Show banner before help output
|
|
100
|
+
const { showAutoBanner } = require("./ui/banner.js");
|
|
101
|
+
showAutoBanner("0.8.0");
|
|
102
|
+
return "";
|
|
103
|
+
});
|
|
85
104
|
|
|
86
105
|
// Create application
|
|
87
106
|
program
|
|
@@ -126,6 +145,14 @@ program
|
|
|
126
145
|
'Base directory for build context (default: "/")',
|
|
127
146
|
"/",
|
|
128
147
|
)
|
|
148
|
+
.option(
|
|
149
|
+
"--github-app-uuid <uuid>",
|
|
150
|
+
"GitHub App UUID (auto-detected if not provided)",
|
|
151
|
+
)
|
|
152
|
+
.option(
|
|
153
|
+
"--domain <domain>",
|
|
154
|
+
"Domain to set after creation (e.g., app.example.com or https://app.example.com)",
|
|
155
|
+
)
|
|
129
156
|
.action(createCommand);
|
|
130
157
|
|
|
131
158
|
// Config command
|
|
@@ -137,6 +164,16 @@ program
|
|
|
137
164
|
.option("--value <value>", 'Configuration value (for "set" action)')
|
|
138
165
|
.action(configCommand);
|
|
139
166
|
|
|
167
|
+
// Init application (link existing or create new)
|
|
168
|
+
program
|
|
169
|
+
.command("init")
|
|
170
|
+
.description("Initialize Coolify deployment (link existing or create new)")
|
|
171
|
+
.option("--yes", "Auto-mode with defaults")
|
|
172
|
+
.option("--force", "Ignore existing .coolify.json")
|
|
173
|
+
.option("--name <name>", "App name")
|
|
174
|
+
.option("--link", "Link-only mode (don't create new apps)")
|
|
175
|
+
.action(initCommand);
|
|
176
|
+
|
|
140
177
|
// List applications
|
|
141
178
|
program
|
|
142
179
|
.command("list")
|
|
@@ -152,6 +189,8 @@ program
|
|
|
152
189
|
.description("Deploy an application (reads .coolify.json if no UUID)")
|
|
153
190
|
.option("-f, --force", "Force rebuild without cache")
|
|
154
191
|
.option("-t, --tag <tag>", "Deploy specific tag/version")
|
|
192
|
+
.option("--all", "Deploy all apps from .coolify.json in parallel")
|
|
193
|
+
.option("--service <name>", "Deploy specific service from .coolify.json")
|
|
155
194
|
.action(deployCommand);
|
|
156
195
|
|
|
157
196
|
// Logs command
|
|
@@ -159,10 +198,17 @@ program
|
|
|
159
198
|
.command("logs [uuid]")
|
|
160
199
|
.description("Get application logs (reads .coolify.json if no UUID)")
|
|
161
200
|
.option("-n, --lines <number>", "Number of lines to retrieve", "50")
|
|
162
|
-
.option("-f, --follow", "Follow logs in real-time")
|
|
201
|
+
.option("-f, --follow", "Follow logs in real-time (polls every 2s)")
|
|
202
|
+
.option("--errors", "Show errors only")
|
|
203
|
+
.option("--since <duration>", "Show logs since duration (e.g. 1h, 30m, 2d)")
|
|
163
204
|
.action((uuid, options) => {
|
|
164
205
|
const lines = parseInt(options.lines, 10);
|
|
165
|
-
logsCommand(uuid, {
|
|
206
|
+
logsCommand(uuid, {
|
|
207
|
+
lines,
|
|
208
|
+
follow: options.follow,
|
|
209
|
+
errors: options.errors,
|
|
210
|
+
since: options.since,
|
|
211
|
+
});
|
|
166
212
|
});
|
|
167
213
|
|
|
168
214
|
// Servers command
|
|
@@ -172,6 +218,13 @@ program
|
|
|
172
218
|
|
|
173
219
|
.action((options) => serversCommand(options));
|
|
174
220
|
|
|
221
|
+
// Status dashboard
|
|
222
|
+
program
|
|
223
|
+
.command("status")
|
|
224
|
+
.description("Show status dashboard")
|
|
225
|
+
.option("-w, --watch", "Auto-refresh every 5s")
|
|
226
|
+
.action((options) => statusCommand(options));
|
|
227
|
+
|
|
175
228
|
// Projects command
|
|
176
229
|
program
|
|
177
230
|
.command("projects")
|
|
@@ -179,6 +232,8 @@ program
|
|
|
179
232
|
|
|
180
233
|
.option("--create <name>", "Create a new project with this name")
|
|
181
234
|
.option("--description <desc>", "Project description (use with --create)")
|
|
235
|
+
.option("--show <uuid>", "Show project details with environments, apps, and databases")
|
|
236
|
+
.option("--apps <uuid>", "Show all applications in a project")
|
|
182
237
|
.action((options) => projectsCommand(options));
|
|
183
238
|
|
|
184
239
|
// Environments command
|
|
@@ -195,6 +250,11 @@ program
|
|
|
195
250
|
.option("--set <KEY=VALUE>", "Set an environment variable")
|
|
196
251
|
.option("--delete <KEY>", "Delete an environment variable")
|
|
197
252
|
.option("--buildtime", "Mark variable as build-time only (use with --set)")
|
|
253
|
+
.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)")
|
|
255
|
+
.option("--dry-run", "Preview changes without applying (use with --sync)")
|
|
256
|
+
.option("--prune", "Delete vars not in file (use with --sync)")
|
|
257
|
+
.option("--table", "Show env vars in table format")
|
|
198
258
|
.action((uuid, options) => envCommand(uuid, options));
|
|
199
259
|
|
|
200
260
|
// Update application
|
|
@@ -224,6 +284,14 @@ program
|
|
|
224
284
|
.option("--auto-deploy", "Enable auto-deploy on git push")
|
|
225
285
|
.option("--no-auto-deploy", "Disable auto-deploy on git push")
|
|
226
286
|
.option("--force-https", "Enable forced HTTPS redirect")
|
|
287
|
+
.option("--health-check-enabled", "Enable health check")
|
|
288
|
+
.option("--no-health-check-enabled", "Disable health check")
|
|
289
|
+
.option("--health-check-path <path>", "Health check path (e.g., /health)")
|
|
290
|
+
.option("--health-check-port <port>", "Health check port")
|
|
291
|
+
.option("--health-check-interval <seconds>", "Health check interval in seconds")
|
|
292
|
+
.option("--health-check-timeout <seconds>", "Health check timeout in seconds")
|
|
293
|
+
.option("--health-check-retries <n>", "Health check retries")
|
|
294
|
+
.option("--health-check-start-period <seconds>", "Health check start period in seconds")
|
|
227
295
|
.action((uuid, options) => updateCommand({ uuid, ...options }));
|
|
228
296
|
|
|
229
297
|
// Delete application
|
|
@@ -335,6 +403,12 @@ db.command("list").description("List all databases").action(dbListCommand);
|
|
|
335
403
|
db.command("get <uuid>")
|
|
336
404
|
.description("Get database details")
|
|
337
405
|
.action(dbGetCommand);
|
|
406
|
+
db.command("update <uuid>")
|
|
407
|
+
.description("Update database configuration")
|
|
408
|
+
.option("--public-port <port>", "Publish port (e.g., 127.0.0.1:5432 or 5432)")
|
|
409
|
+
.option("--is-public", "Make database publicly accessible")
|
|
410
|
+
.option("--no-is-public", "Make database private")
|
|
411
|
+
.action((uuid, options) => dbUpdateCommand(uuid, options));
|
|
338
412
|
db.command("create <type>")
|
|
339
413
|
.description(
|
|
340
414
|
"Create database (postgresql, mysql, mariadb, mongodb, redis, keydb, clickhouse, dragonfly)",
|
|
@@ -479,11 +553,18 @@ program
|
|
|
479
553
|
.description("Analyze a failed deployment (extract errors, suggest fixes)")
|
|
480
554
|
.action(analyzeDeployCommand);
|
|
481
555
|
|
|
482
|
-
// Show help by default
|
|
483
|
-
program.action(() => {
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
556
|
+
// Show help by default (or interactive menu if no args)
|
|
557
|
+
program.action(async () => {
|
|
558
|
+
// If called without arguments, show interactive menu
|
|
559
|
+
if (process.argv.length <= 2) {
|
|
560
|
+
const { mainMenu } = await import("./commands/main-menu.js");
|
|
561
|
+
await mainMenu();
|
|
562
|
+
} else {
|
|
563
|
+
// Show help if called with unknown command
|
|
564
|
+
console.log(chalk.cyan("Coolify MCP CLI v0.8.0"));
|
|
565
|
+
console.log(chalk.gray("Manage Coolify deployments from the command line\n"));
|
|
566
|
+
program.help();
|
|
567
|
+
}
|
|
487
568
|
});
|
|
488
569
|
|
|
489
570
|
program.parse();
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Name-to-UUID resolver for CLI.
|
|
3
|
+
*
|
|
4
|
+
* Allows users to reference Coolify resources by friendly name instead
|
|
5
|
+
* of UUID. Caches API responses in-memory during the CLI session.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { isOk } from "@mks2508/no-throw";
|
|
11
|
+
import { getCoolifyService } from "../coolify/index.js";
|
|
12
|
+
import type { ICoolifyApplication } from "../coolify/types.js";
|
|
13
|
+
import type { ICoolifyDatabase } from "../coolify/types.js";
|
|
14
|
+
import type { ICoolifyProject } from "../coolify/types.js";
|
|
15
|
+
import type { ICoolifyService as ICoolifyServiceType } from "../coolify/types.js";
|
|
16
|
+
|
|
17
|
+
/** In-memory cache for API responses during a single CLI session. */
|
|
18
|
+
const cache = {
|
|
19
|
+
apps: null as ICoolifyApplication[] | null,
|
|
20
|
+
databases: null as ICoolifyDatabase[] | null,
|
|
21
|
+
services: null as ICoolifyServiceType[] | null,
|
|
22
|
+
projects: null as ICoolifyProject[] | null,
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if a string looks like a Coolify UUID.
|
|
27
|
+
*
|
|
28
|
+
* Coolify UUIDs are lowercase alphanumeric strings, typically 20-30 chars.
|
|
29
|
+
*
|
|
30
|
+
* @param value - The string to check
|
|
31
|
+
* @returns True if the string looks like a UUID
|
|
32
|
+
*/
|
|
33
|
+
function looksLikeUuid(value: string): boolean {
|
|
34
|
+
return /^[a-z0-9]{16,40}$/.test(value);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Fetches and caches the list of applications from Coolify API.
|
|
39
|
+
*
|
|
40
|
+
* @returns Array of applications, or empty array on error
|
|
41
|
+
*/
|
|
42
|
+
async function getApps(): Promise<ICoolifyApplication[]> {
|
|
43
|
+
if (cache.apps) return cache.apps;
|
|
44
|
+
|
|
45
|
+
const coolify = getCoolifyService();
|
|
46
|
+
const initResult = await coolify.init();
|
|
47
|
+
if (isOk(initResult)) {
|
|
48
|
+
const result = await coolify.listApplications();
|
|
49
|
+
if (isOk(result)) {
|
|
50
|
+
cache.apps = result.value;
|
|
51
|
+
return cache.apps;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fetches and caches the list of databases from Coolify API.
|
|
59
|
+
*
|
|
60
|
+
* @returns Array of databases, or empty array on error
|
|
61
|
+
*/
|
|
62
|
+
async function getDatabases(): Promise<ICoolifyDatabase[]> {
|
|
63
|
+
if (cache.databases) return cache.databases;
|
|
64
|
+
|
|
65
|
+
const coolify = getCoolifyService();
|
|
66
|
+
const initResult = await coolify.init();
|
|
67
|
+
if (isOk(initResult)) {
|
|
68
|
+
const result = await coolify.listDatabases();
|
|
69
|
+
if (isOk(result)) {
|
|
70
|
+
cache.databases = result.value;
|
|
71
|
+
return cache.databases;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Fetches and caches the list of services from Coolify API.
|
|
79
|
+
*
|
|
80
|
+
* @returns Array of services, or empty array on error
|
|
81
|
+
*/
|
|
82
|
+
async function getServices(): Promise<ICoolifyServiceType[]> {
|
|
83
|
+
if (cache.services) return cache.services;
|
|
84
|
+
|
|
85
|
+
const coolify = getCoolifyService();
|
|
86
|
+
const initResult = await coolify.init();
|
|
87
|
+
if (isOk(initResult)) {
|
|
88
|
+
const result = await coolify.listServices();
|
|
89
|
+
if (isOk(result)) {
|
|
90
|
+
cache.services = result.value;
|
|
91
|
+
return cache.services;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Resolves a name or UUID to a UUID.
|
|
99
|
+
*
|
|
100
|
+
* Accepts: UUID directly, app name, database name, or service name.
|
|
101
|
+
* Searches applications first, then databases, then services.
|
|
102
|
+
* Results are cached in-memory for the duration of the CLI session.
|
|
103
|
+
*
|
|
104
|
+
* @param nameOrUuid - A friendly name or UUID string
|
|
105
|
+
* @returns The resolved UUID, or null if not found
|
|
106
|
+
*/
|
|
107
|
+
export async function resolveNameOrUuid(
|
|
108
|
+
nameOrUuid: string,
|
|
109
|
+
): Promise<string | null> {
|
|
110
|
+
if (looksLikeUuid(nameOrUuid)) return nameOrUuid;
|
|
111
|
+
|
|
112
|
+
const query = nameOrUuid.toLowerCase();
|
|
113
|
+
|
|
114
|
+
// Search applications
|
|
115
|
+
const apps = await getApps();
|
|
116
|
+
const app = apps.find((a) => a.name?.toLowerCase() === query);
|
|
117
|
+
if (app) return app.uuid;
|
|
118
|
+
|
|
119
|
+
// Search databases
|
|
120
|
+
const databases = await getDatabases();
|
|
121
|
+
const db = databases.find((d) => d.name?.toLowerCase() === query);
|
|
122
|
+
if (db) return db.uuid;
|
|
123
|
+
|
|
124
|
+
// Search services
|
|
125
|
+
const services = await getServices();
|
|
126
|
+
const svc = services.find((s) => s.name?.toLowerCase() === query);
|
|
127
|
+
if (svc) return svc.uuid;
|
|
128
|
+
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Resolves a name or UUID specifically for applications.
|
|
134
|
+
*
|
|
135
|
+
* @param nameOrUuid - A friendly name or UUID string
|
|
136
|
+
* @returns The resolved application UUID, or null if not found
|
|
137
|
+
*/
|
|
138
|
+
export async function resolveAppNameOrUuid(
|
|
139
|
+
nameOrUuid: string,
|
|
140
|
+
): Promise<string | null> {
|
|
141
|
+
if (looksLikeUuid(nameOrUuid)) return nameOrUuid;
|
|
142
|
+
|
|
143
|
+
const query = nameOrUuid.toLowerCase();
|
|
144
|
+
const apps = await getApps();
|
|
145
|
+
const app = apps.find((a) => a.name?.toLowerCase() === query);
|
|
146
|
+
return app?.uuid ?? null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Resolves a name or UUID specifically for databases.
|
|
151
|
+
*
|
|
152
|
+
* @param nameOrUuid - A friendly name or UUID string
|
|
153
|
+
* @returns The resolved database UUID, or null if not found
|
|
154
|
+
*/
|
|
155
|
+
export async function resolveDbNameOrUuid(
|
|
156
|
+
nameOrUuid: string,
|
|
157
|
+
): Promise<string | null> {
|
|
158
|
+
if (looksLikeUuid(nameOrUuid)) return nameOrUuid;
|
|
159
|
+
|
|
160
|
+
const query = nameOrUuid.toLowerCase();
|
|
161
|
+
const databases = await getDatabases();
|
|
162
|
+
const db = databases.find((d) => d.name?.toLowerCase() === query);
|
|
163
|
+
return db?.uuid ?? null;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Fetches and caches the list of projects from Coolify API.
|
|
168
|
+
*
|
|
169
|
+
* @returns Array of projects, or empty array on error
|
|
170
|
+
*/
|
|
171
|
+
async function getProjects(): Promise<ICoolifyProject[]> {
|
|
172
|
+
if (cache.projects) return cache.projects;
|
|
173
|
+
|
|
174
|
+
const coolify = getCoolifyService();
|
|
175
|
+
const initResult = await coolify.init();
|
|
176
|
+
if (isOk(initResult)) {
|
|
177
|
+
const result = await coolify.listProjects();
|
|
178
|
+
if (isOk(result)) {
|
|
179
|
+
cache.projects = result.value;
|
|
180
|
+
return cache.projects;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return [];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Resolves a name or UUID specifically for projects.
|
|
188
|
+
*
|
|
189
|
+
* @param nameOrUuid - A friendly name or UUID string
|
|
190
|
+
* @returns The resolved project UUID, or null if not found
|
|
191
|
+
*/
|
|
192
|
+
export async function resolveProjectNameOrUuid(
|
|
193
|
+
nameOrUuid: string,
|
|
194
|
+
): Promise<string | null> {
|
|
195
|
+
if (looksLikeUuid(nameOrUuid)) return nameOrUuid;
|
|
196
|
+
|
|
197
|
+
const query = nameOrUuid.toLowerCase();
|
|
198
|
+
const projects = await getProjects();
|
|
199
|
+
const project = projects.find((p) => p.name?.toLowerCase() === query);
|
|
200
|
+
return project?.uuid ?? null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Resolves a name or UUID specifically for services.
|
|
205
|
+
*
|
|
206
|
+
* @param nameOrUuid - A friendly name or UUID string
|
|
207
|
+
* @returns The resolved service UUID, or null if not found
|
|
208
|
+
*/
|
|
209
|
+
export async function resolveSvcNameOrUuid(
|
|
210
|
+
nameOrUuid: string,
|
|
211
|
+
): Promise<string | null> {
|
|
212
|
+
if (looksLikeUuid(nameOrUuid)) return nameOrUuid;
|
|
213
|
+
|
|
214
|
+
const query = nameOrUuid.toLowerCase();
|
|
215
|
+
const services = await getServices();
|
|
216
|
+
const svc = services.find((s) => s.name?.toLowerCase() === query);
|
|
217
|
+
return svc?.uuid ?? null;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Clears the in-memory cache. Useful for testing or long-running sessions.
|
|
222
|
+
*/
|
|
223
|
+
export function clearResolverCache(): void {
|
|
224
|
+
cache.apps = null;
|
|
225
|
+
cache.databases = null;
|
|
226
|
+
cache.services = null;
|
|
227
|
+
cache.projects = null;
|
|
228
|
+
}
|